2 * Part of Very Secure FTPd
10 #include "oneprocess.h"
11 #include "twoprocess.h"
14 #include "ftpdataio.h"
23 #include "sysdeputil.h"
24 #include "ipaddrparse.h"
31 /* Private local functions */
32 static void handle_pwd(struct vsf_session
* p_sess
);
33 static void handle_cwd(struct vsf_session
* p_sess
);
34 static void handle_pasv(struct vsf_session
* p_sess
, int is_epsv
);
35 static void handle_retr(struct vsf_session
* p_sess
, int is_http
);
36 static void handle_cdup(struct vsf_session
* p_sess
);
37 static void handle_list(struct vsf_session
* p_sess
);
38 static void handle_type(struct vsf_session
* p_sess
);
39 static void handle_port(struct vsf_session
* p_sess
);
40 static void handle_stor(struct vsf_session
* p_sess
);
41 static void handle_mkd(struct vsf_session
* p_sess
);
42 static void handle_rmd(struct vsf_session
* p_sess
);
43 static void handle_dele(struct vsf_session
* p_sess
);
44 static void handle_rest(struct vsf_session
* p_sess
);
45 static void handle_rnfr(struct vsf_session
* p_sess
);
46 static void handle_rnto(struct vsf_session
* p_sess
);
47 static void handle_nlst(struct vsf_session
* p_sess
);
48 static void handle_size(struct vsf_session
* p_sess
);
49 static void handle_site(struct vsf_session
* p_sess
);
50 static void handle_appe(struct vsf_session
* p_sess
);
51 static void handle_mdtm(struct vsf_session
* p_sess
);
52 static void handle_site_chmod(struct vsf_session
* p_sess
,
53 struct mystr
* p_arg_str
);
54 static void handle_site_umask(struct vsf_session
* p_sess
,
55 struct mystr
* p_arg_str
);
56 static void handle_eprt(struct vsf_session
* p_sess
);
57 static void handle_help(struct vsf_session
* p_sess
);
58 static void handle_stou(struct vsf_session
* p_sess
);
59 static void handle_stat(struct vsf_session
* p_sess
);
60 static void handle_stat_file(struct vsf_session
* p_sess
);
61 static void handle_logged_in_user(struct vsf_session
* p_sess
);
62 static void handle_logged_in_pass(struct vsf_session
* p_sess
);
63 static void handle_http(struct vsf_session
* p_sess
);
65 static int pasv_active(struct vsf_session
* p_sess
);
66 static int port_active(struct vsf_session
* p_sess
);
67 static void pasv_cleanup(struct vsf_session
* p_sess
);
68 static void port_cleanup(struct vsf_session
* p_sess
);
69 static void handle_dir_common(struct vsf_session
* p_sess
, int full_details
,
71 static void prepend_path_to_filename(struct mystr
* p_str
);
72 static int get_remote_transfer_fd(struct vsf_session
* p_sess
,
73 const char* p_status_msg
);
74 static void check_abor(struct vsf_session
* p_sess
);
75 static void handle_sigurg(void* p_private
);
76 static void handle_upload_common(struct vsf_session
* p_sess
, int is_append
,
78 static void get_unique_filename(struct mystr
* p_outstr
,
79 const struct mystr
* p_base
);
80 static int data_transfer_checks_ok(struct vsf_session
* p_sess
);
81 static void resolve_tilde(struct mystr
* p_str
, struct vsf_session
* p_sess
);
84 process_post_login(struct vsf_session
* p_sess
)
86 str_getcwd(&p_sess
->home_str
);
87 if (p_sess
->is_anonymous
)
89 vsf_sysutil_set_umask(tunable_anon_umask
);
90 p_sess
->bw_rate_max
= tunable_anon_max_rate
;
94 vsf_sysutil_set_umask(tunable_local_umask
);
95 p_sess
->bw_rate_max
= tunable_local_max_rate
;
100 bug("should not be reached");
103 /* Don't support async ABOR if we have an SSL channel. The spec says SHOULD
104 * NOT, and I think there are synchronization issues between command and
107 if (tunable_async_abor_enable
&& !p_sess
->control_use_ssl
)
109 vsf_sysutil_install_sighandler(kVSFSysUtilSigURG
, handle_sigurg
, p_sess
, 0);
110 vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD
);
112 /* Handle any login message */
113 vsf_banner_dir_changed(p_sess
, FTP_LOGINOK
);
114 vsf_cmdio_write(p_sess
, FTP_LOGINOK
, "Login successful.");
119 if (tunable_setproctitle_enable
)
121 vsf_sysutil_setproctitle("IDLE");
124 vsf_cmdio_get_cmd_and_arg(p_sess
, &p_sess
->ftp_cmd_str
,
125 &p_sess
->ftp_arg_str
, 1);
126 if (tunable_setproctitle_enable
)
128 struct mystr proctitle_str
= INIT_MYSTR
;
129 str_copy(&proctitle_str
, &p_sess
->ftp_cmd_str
);
130 if (!str_isempty(&p_sess
->ftp_arg_str
))
132 str_append_char(&proctitle_str
, ' ');
133 str_append_str(&proctitle_str
, &p_sess
->ftp_arg_str
);
135 /* Suggestion from Solar */
136 str_replace_unprintable(&proctitle_str
, '?');
137 vsf_sysutil_setproctitle_str(&proctitle_str
);
138 str_free(&proctitle_str
);
140 /* Test command against the allowed lists.. */
141 if (tunable_cmds_allowed
)
143 static struct mystr s_src_str
;
144 static struct mystr s_rhs_str
;
145 str_alloc_text(&s_src_str
, tunable_cmds_allowed
);
148 str_split_char(&s_src_str
, &s_rhs_str
, ',');
149 if (str_isempty(&s_src_str
))
154 else if (str_equal(&s_src_str
, &p_sess
->ftp_cmd_str
))
158 str_copy(&s_src_str
, &s_rhs_str
);
161 if (tunable_cmds_denied
)
163 static struct mystr s_src_str
;
164 static struct mystr s_rhs_str
;
165 str_alloc_text(&s_src_str
, tunable_cmds_denied
);
168 str_split_char(&s_src_str
, &s_rhs_str
, ',');
169 if (str_isempty(&s_src_str
))
173 else if (str_equal(&s_src_str
, &p_sess
->ftp_cmd_str
))
178 str_copy(&s_src_str
, &s_rhs_str
);
183 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
185 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "QUIT"))
187 vsf_cmdio_write_exit(p_sess
, FTP_GOODBYE
, "Goodbye.", 0);
189 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PWD") ||
190 str_equal_text(&p_sess
->ftp_cmd_str
, "XPWD"))
194 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "CWD") ||
195 str_equal_text(&p_sess
->ftp_cmd_str
, "XCWD"))
199 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "CDUP") ||
200 str_equal_text(&p_sess
->ftp_cmd_str
, "XCUP"))
204 else if (tunable_pasv_enable
&&
206 (str_equal_text(&p_sess
->ftp_cmd_str
, "PASV") ||
207 str_equal_text(&p_sess
->ftp_cmd_str
, "P@SW")))
209 handle_pasv(p_sess
, 0);
211 else if (tunable_pasv_enable
&&
212 str_equal_text(&p_sess
->ftp_cmd_str
, "EPSV"))
214 handle_pasv(p_sess
, 1);
216 else if (tunable_download_enable
&&
217 str_equal_text(&p_sess
->ftp_cmd_str
, "RETR"))
219 handle_retr(p_sess
, 0);
221 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "NOOP"))
223 vsf_cmdio_write(p_sess
, FTP_NOOPOK
, "NOOP ok.");
225 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SYST"))
227 vsf_cmdio_write(p_sess
, FTP_SYSTOK
, "UNIX Type: L8");
229 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "HELP"))
233 else if (tunable_dirlist_enable
&&
234 str_equal_text(&p_sess
->ftp_cmd_str
, "LIST"))
238 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "TYPE"))
242 else if (tunable_port_enable
&&
244 str_equal_text(&p_sess
->ftp_cmd_str
, "PORT"))
248 else if (tunable_write_enable
&&
249 (tunable_anon_upload_enable
|| !p_sess
->is_anonymous
) &&
250 str_equal_text(&p_sess
->ftp_cmd_str
, "STOR"))
254 else if (tunable_write_enable
&&
255 (tunable_anon_mkdir_write_enable
|| !p_sess
->is_anonymous
) &&
256 (str_equal_text(&p_sess
->ftp_cmd_str
, "MKD") ||
257 str_equal_text(&p_sess
->ftp_cmd_str
, "XMKD")))
261 else if (tunable_write_enable
&&
262 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
263 (str_equal_text(&p_sess
->ftp_cmd_str
, "RMD") ||
264 str_equal_text(&p_sess
->ftp_cmd_str
, "XRMD")))
268 else if (tunable_write_enable
&&
269 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
270 str_equal_text(&p_sess
->ftp_cmd_str
, "DELE"))
274 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "REST"))
278 else if (tunable_write_enable
&&
279 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
280 str_equal_text(&p_sess
->ftp_cmd_str
, "RNFR"))
284 else if (tunable_write_enable
&&
285 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
286 str_equal_text(&p_sess
->ftp_cmd_str
, "RNTO"))
290 else if (tunable_dirlist_enable
&&
291 str_equal_text(&p_sess
->ftp_cmd_str
, "NLST"))
295 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SIZE"))
299 else if (!p_sess
->is_anonymous
&&
300 str_equal_text(&p_sess
->ftp_cmd_str
, "SITE"))
304 /* Note - the weird ABOR string is checking for an async ABOR arriving
305 * without a SIGURG condition.
307 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ABOR") ||
308 str_equal_text(&p_sess
->ftp_cmd_str
, "\377\364\377\362ABOR"))
310 vsf_cmdio_write(p_sess
, FTP_ABOR_NOCONN
, "No transfer to ABOR.");
312 else if (tunable_write_enable
&&
313 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
314 str_equal_text(&p_sess
->ftp_cmd_str
, "APPE"))
318 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "MDTM"))
322 else if (tunable_port_enable
&&
323 str_equal_text(&p_sess
->ftp_cmd_str
, "EPRT"))
327 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "STRU"))
329 str_upper(&p_sess
->ftp_arg_str
);
330 if (str_equal_text(&p_sess
->ftp_arg_str
, "F"))
332 vsf_cmdio_write(p_sess
, FTP_STRUOK
, "Structure set to F.");
336 vsf_cmdio_write(p_sess
, FTP_BADSTRU
, "Bad STRU command.");
339 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "MODE"))
341 str_upper(&p_sess
->ftp_arg_str
);
342 if (str_equal_text(&p_sess
->ftp_arg_str
, "S"))
344 vsf_cmdio_write(p_sess
, FTP_MODEOK
, "Mode set to S.");
348 vsf_cmdio_write(p_sess
, FTP_BADMODE
, "Bad MODE command.");
351 else if (tunable_write_enable
&&
352 (tunable_anon_upload_enable
|| !p_sess
->is_anonymous
) &&
353 str_equal_text(&p_sess
->ftp_cmd_str
, "STOU"))
357 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ALLO"))
359 vsf_cmdio_write(p_sess
, FTP_ALLOOK
, "ALLO command ignored.");
361 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "REIN"))
363 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "REIN not implemented.");
365 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ACCT"))
367 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "ACCT not implemented.");
369 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SMNT"))
371 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "SMNT not implemented.");
373 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "FEAT"))
377 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "OPTS"))
381 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "STAT") &&
382 str_isempty(&p_sess
->ftp_arg_str
))
386 else if (tunable_dirlist_enable
&&
387 str_equal_text(&p_sess
->ftp_cmd_str
, "STAT"))
389 handle_stat_file(p_sess
);
391 else if (tunable_ssl_enable
&& str_equal_text(&p_sess
->ftp_cmd_str
, "PBSZ"))
395 else if (tunable_ssl_enable
&& str_equal_text(&p_sess
->ftp_cmd_str
, "PROT"))
399 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "USER"))
401 handle_logged_in_user(p_sess
);
403 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PASS"))
405 handle_logged_in_pass(p_sess
);
407 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PASV") ||
408 str_equal_text(&p_sess
->ftp_cmd_str
, "PORT") ||
409 str_equal_text(&p_sess
->ftp_cmd_str
, "STOR") ||
410 str_equal_text(&p_sess
->ftp_cmd_str
, "MKD") ||
411 str_equal_text(&p_sess
->ftp_cmd_str
, "XMKD") ||
412 str_equal_text(&p_sess
->ftp_cmd_str
, "RMD") ||
413 str_equal_text(&p_sess
->ftp_cmd_str
, "XRMD") ||
414 str_equal_text(&p_sess
->ftp_cmd_str
, "DELE") ||
415 str_equal_text(&p_sess
->ftp_cmd_str
, "RNFR") ||
416 str_equal_text(&p_sess
->ftp_cmd_str
, "RNTO") ||
417 str_equal_text(&p_sess
->ftp_cmd_str
, "SITE") ||
418 str_equal_text(&p_sess
->ftp_cmd_str
, "APPE") ||
419 str_equal_text(&p_sess
->ftp_cmd_str
, "EPSV") ||
420 str_equal_text(&p_sess
->ftp_cmd_str
, "EPRT") ||
421 str_equal_text(&p_sess
->ftp_cmd_str
, "RETR") ||
422 str_equal_text(&p_sess
->ftp_cmd_str
, "LIST") ||
423 str_equal_text(&p_sess
->ftp_cmd_str
, "NLST") ||
424 str_equal_text(&p_sess
->ftp_cmd_str
, "STOU") ||
425 str_equal_text(&p_sess
->ftp_cmd_str
, "ALLO") ||
426 str_equal_text(&p_sess
->ftp_cmd_str
, "REIN") ||
427 str_equal_text(&p_sess
->ftp_cmd_str
, "ACCT") ||
428 str_equal_text(&p_sess
->ftp_cmd_str
, "SMNT") ||
429 str_equal_text(&p_sess
->ftp_cmd_str
, "FEAT") ||
430 str_equal_text(&p_sess
->ftp_cmd_str
, "OPTS") ||
431 str_equal_text(&p_sess
->ftp_cmd_str
, "STAT") ||
432 str_equal_text(&p_sess
->ftp_cmd_str
, "PBSZ") ||
433 str_equal_text(&p_sess
->ftp_cmd_str
, "PROT"))
435 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
437 else if (str_isempty(&p_sess
->ftp_cmd_str
) &&
438 str_isempty(&p_sess
->ftp_arg_str
))
440 /* Deliberately ignore to avoid NAT device bugs. ProFTPd does the same. */
444 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown command.");
446 if (vsf_log_entry_pending(p_sess
))
448 vsf_log_do_log(p_sess
, 0);
450 if (p_sess
->data_timeout
)
452 vsf_cmdio_write_exit(p_sess
, FTP_DATA_TIMEOUT
,
453 "Data timeout. Reconnect. Sorry.", 1);
459 handle_pwd(struct vsf_session
* p_sess
)
461 static struct mystr s_cwd_buf_mangle_str
;
462 static struct mystr s_pwd_res_str
;
463 str_getcwd(&s_cwd_buf_mangle_str
);
464 /* Double up any double-quotes in the pathname! */
465 str_replace_text(&s_cwd_buf_mangle_str
, "\"", "\"\"");
466 /* Enclose pathname in quotes */
467 str_alloc_text(&s_pwd_res_str
, "\"");
468 str_append_str(&s_pwd_res_str
, &s_cwd_buf_mangle_str
);
469 str_append_text(&s_pwd_res_str
, "\"");
470 vsf_cmdio_write_str(p_sess
, FTP_PWDOK
, &s_pwd_res_str
);
474 handle_cwd(struct vsf_session
* p_sess
)
477 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
478 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
480 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
483 retval
= str_chdir(&p_sess
->ftp_arg_str
);
486 /* Handle any messages */
487 vsf_banner_dir_changed(p_sess
, FTP_CWDOK
);
488 vsf_cmdio_write(p_sess
, FTP_CWDOK
, "Directory successfully changed.");
492 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to change directory.");
497 handle_cdup(struct vsf_session
* p_sess
)
499 str_alloc_text(&p_sess
->ftp_arg_str
, "..");
504 port_active(struct vsf_session
* p_sess
)
507 if (p_sess
->p_port_sockaddr
!= 0)
510 if (pasv_active(p_sess
))
512 bug("port and pasv both active");
519 pasv_active(struct vsf_session
* p_sess
)
522 if (tunable_one_process_model
)
524 ret
= vsf_one_process_pasv_active(p_sess
);
528 ret
= vsf_two_process_pasv_active(p_sess
);
532 if (port_active(p_sess
))
534 bug("pasv and port both active");
541 port_cleanup(struct vsf_session
* p_sess
)
543 vsf_sysutil_sockaddr_clear(&p_sess
->p_port_sockaddr
);
547 pasv_cleanup(struct vsf_session
* p_sess
)
549 if (tunable_one_process_model
)
551 vsf_one_process_pasv_cleanup(p_sess
);
555 vsf_two_process_pasv_cleanup(p_sess
);
560 handle_pasv(struct vsf_session
* p_sess
, int is_epsv
)
562 unsigned short the_port
;
563 static struct mystr s_pasv_res_str
;
564 static struct vsf_sysutil_sockaddr
* s_p_sockaddr
;
565 int is_ipv6
= vsf_sysutil_sockaddr_is_ipv6(p_sess
->p_local_addr
);
566 if (is_epsv
&& !str_isempty(&p_sess
->ftp_arg_str
))
569 str_upper(&p_sess
->ftp_arg_str
);
570 if (str_equal_text(&p_sess
->ftp_arg_str
, "ALL"))
572 p_sess
->epsv_all
= 1;
573 vsf_cmdio_write(p_sess
, FTP_EPSVALLOK
, "EPSV ALL ok.");
576 argval
= vsf_sysutil_atoi(str_getbuf(&p_sess
->ftp_arg_str
));
577 if (argval
< 1 || argval
> 2 || (!is_ipv6
&& argval
== 2))
579 vsf_cmdio_write(p_sess
, FTP_EPSVBAD
, "Bad network protocol.");
583 pasv_cleanup(p_sess
);
584 port_cleanup(p_sess
);
585 if (tunable_one_process_model
)
587 the_port
= vsf_one_process_listen(p_sess
);
591 the_port
= vsf_two_process_listen(p_sess
);
595 str_alloc_text(&s_pasv_res_str
, "Entering Extended Passive Mode (|||");
596 str_append_ulong(&s_pasv_res_str
, (unsigned long) the_port
);
597 str_append_text(&s_pasv_res_str
, "|).");
598 vsf_cmdio_write_str(p_sess
, FTP_EPSVOK
, &s_pasv_res_str
);
601 if (tunable_pasv_address
!= 0)
603 vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr
);
604 /* Report passive address as specified in configuration */
605 if (vsf_sysutil_inet_aton(tunable_pasv_address
, s_p_sockaddr
) == 0)
607 die("invalid pasv_address");
612 vsf_sysutil_sockaddr_clone(&s_p_sockaddr
, p_sess
->p_local_addr
);
614 str_alloc_text(&s_pasv_res_str
, "Entering Passive Mode (");
617 str_append_text(&s_pasv_res_str
, vsf_sysutil_inet_ntop(s_p_sockaddr
));
621 const void* p_v4addr
= vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr
);
624 str_append_text(&s_pasv_res_str
, vsf_sysutil_inet_ntoa(p_v4addr
));
628 str_append_text(&s_pasv_res_str
, "0,0,0,0");
631 str_replace_char(&s_pasv_res_str
, '.', ',');
632 str_append_text(&s_pasv_res_str
, ",");
633 str_append_ulong(&s_pasv_res_str
, the_port
>> 8);
634 str_append_text(&s_pasv_res_str
, ",");
635 str_append_ulong(&s_pasv_res_str
, the_port
& 255);
636 str_append_text(&s_pasv_res_str
, ").");
637 vsf_cmdio_write_str(p_sess
, FTP_PASVOK
, &s_pasv_res_str
);
641 handle_retr(struct vsf_session
* p_sess
, int is_http
)
643 static struct mystr s_mark_str
;
644 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
645 struct vsf_transfer_ret trans_ret
;
649 filesize_t offset
= p_sess
->restart_pos
;
650 p_sess
->restart_pos
= 0;
651 if (!is_http
&& !data_transfer_checks_ok(p_sess
))
655 if (p_sess
->is_ascii
&& offset
!= 0)
657 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
658 "No support for resume of ASCII transfer.");
661 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
662 vsf_log_start_entry(p_sess
, kVSFLogEntryDownload
);
663 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
664 prepend_path_to_filename(&p_sess
->log_str
);
665 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
667 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
670 opened_file
= str_open(&p_sess
->ftp_arg_str
, kVSFSysStrOpenReadOnly
);
671 if (vsf_sysutil_retval_is_error(opened_file
))
673 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
676 /* Lock file if required */
677 if (tunable_lock_upload_files
)
679 vsf_sysutil_lock_file_read(opened_file
);
681 vsf_sysutil_fstat(opened_file
, &s_p_statbuf
);
682 /* No games please */
683 if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
685 /* Note - pretend open failed */
686 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
687 /* Irritating FireFox does RETR on directories, so avoid logging this
688 * very common and noisy case.
690 if (vsf_sysutil_statbuf_is_dir(s_p_statbuf
))
692 vsf_log_clear_entry(p_sess
);
696 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
699 vsf_sysutil_deactivate_noblock(opened_file
);
700 /* Optionally, we'll be paranoid and only serve publicly readable stuff */
701 if (p_sess
->is_anonymous
&& tunable_anon_world_readable_only
&&
702 !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf
))
704 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
707 /* Set the download offset (from REST) if any */
710 vsf_sysutil_lseek_to(opened_file
, offset
);
712 str_alloc_text(&s_mark_str
, "Opening ");
713 if (tunable_ascii_download_enable
&& p_sess
->is_ascii
)
715 str_append_text(&s_mark_str
, "ASCII");
720 str_append_text(&s_mark_str
, "BINARY");
722 str_append_text(&s_mark_str
, " mode data connection for ");
723 str_append_str(&s_mark_str
, &p_sess
->ftp_arg_str
);
724 str_append_text(&s_mark_str
, " (");
725 str_append_filesize_t(&s_mark_str
,
726 vsf_sysutil_statbuf_get_size(s_p_statbuf
));
727 str_append_text(&s_mark_str
, " bytes).");
730 remote_fd
= VSFTP_COMMAND_FD
;
734 remote_fd
= get_remote_transfer_fd(p_sess
, str_getbuf(&s_mark_str
));
735 if (vsf_sysutil_retval_is_error(remote_fd
))
737 goto port_pasv_cleanup_out
;
740 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
741 opened_file
, 0, is_ascii
);
743 vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 &&
744 trans_ret
.retval
== 0)
746 trans_ret
.retval
= -2;
748 p_sess
->transfer_size
= trans_ret
.transferred
;
749 /* Log _after_ the blocking dispose call, so we get transfer times right */
750 if (trans_ret
.retval
== 0)
752 vsf_log_do_log(p_sess
, 1);
758 /* Emit status message _after_ blocking dispose call to avoid buggy FTP
759 * clients truncating the transfer.
761 if (trans_ret
.retval
== -1)
763 vsf_cmdio_write(p_sess
, FTP_BADSENDFILE
, "Failure reading local file.");
765 else if (trans_ret
.retval
== -2)
767 if (!p_sess
->data_timeout
)
769 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
,
770 "Failure writing network stream.");
775 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Transfer complete.");
778 port_pasv_cleanup_out
:
779 port_cleanup(p_sess
);
780 pasv_cleanup(p_sess
);
782 vsf_sysutil_close(opened_file
);
786 handle_list(struct vsf_session
* p_sess
)
788 handle_dir_common(p_sess
, 1, 0);
792 handle_dir_common(struct vsf_session
* p_sess
, int full_details
, int stat_cmd
)
794 static struct mystr s_option_str
;
795 static struct mystr s_filter_str
;
796 static struct mystr s_dir_name_str
;
797 static struct vsf_sysutil_statbuf
* s_p_dirstat
;
798 int dir_allow_read
= 1;
799 struct vsf_sysutil_dir
* p_dir
= 0;
802 str_empty(&s_option_str
);
803 str_empty(&s_filter_str
);
804 /* By default open the current directory */
805 str_alloc_text(&s_dir_name_str
, ".");
806 if (!stat_cmd
&& !data_transfer_checks_ok(p_sess
))
810 /* Do we have an option? Going to be strict here - the option must come
811 * first. e.g. "ls -a .." fine, "ls .. -a" not fine
813 if (!str_isempty(&p_sess
->ftp_arg_str
) &&
814 str_get_char_at(&p_sess
->ftp_arg_str
, 0) == '-')
816 /* Chop off the '-' */
817 str_mid_to_end(&p_sess
->ftp_arg_str
, &s_option_str
, 1);
818 /* A space will separate options from filter (if any) */
819 str_split_char(&s_option_str
, &s_filter_str
, ' ');
823 /* The argument, if any, is just a filter */
824 str_copy(&s_filter_str
, &p_sess
->ftp_arg_str
);
826 if (!str_isempty(&s_filter_str
))
828 resolve_tilde(&s_filter_str
, p_sess
);
829 if (!vsf_access_check_file(&s_filter_str
))
831 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
834 /* First check - is it an outright directory, as in "ls /pub" */
835 p_dir
= str_opendir(&s_filter_str
);
838 /* Listing a directory! */
839 str_copy(&s_dir_name_str
, &s_filter_str
);
840 str_free(&s_filter_str
);
844 struct str_locate_result locate_result
=
845 str_locate_char(&s_filter_str
, '/');
846 if (locate_result
.found
)
848 /* Includes a path! Reverse scan for / in the arg, to get the
849 * base directory and filter (if any)
851 str_copy(&s_dir_name_str
, &s_filter_str
);
852 str_split_char_reverse(&s_dir_name_str
, &s_filter_str
, '/');
853 /* If we have e.g. "ls /.message", we just ripped off the leading
854 * slash because it is the only one!
856 if (str_isempty(&s_dir_name_str
))
858 str_alloc_text(&s_dir_name_str
, "/");
865 /* NOTE - failure check done below, it's not forgotten */
866 p_dir
= str_opendir(&s_dir_name_str
);
872 str_append_char(&s_option_str
, 'a');
873 vsf_cmdio_write_hyphen(p_sess
, FTP_STATFILE_OK
, "Status follows:");
877 int remote_fd
= get_remote_transfer_fd(
878 p_sess
, "Here comes the directory listing.");
879 if (vsf_sysutil_retval_is_error(remote_fd
))
884 if (p_sess
->is_anonymous
&& p_dir
&& tunable_anon_world_readable_only
)
886 vsf_sysutil_dir_stat(p_dir
, &s_p_dirstat
);
887 if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat
))
892 if (p_dir
!= 0 && dir_allow_read
)
894 retval
= vsf_ftpdataio_transfer_dir(p_sess
, use_control
, p_dir
,
895 &s_dir_name_str
, &s_option_str
,
896 &s_filter_str
, full_details
);
900 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && retval
== 0)
907 vsf_cmdio_write(p_sess
, FTP_STATFILE_OK
, "End of status");
909 else if (retval
!= 0)
911 if (!p_sess
->data_timeout
)
913 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
,
914 "Failure writing network stream.");
917 else if (p_dir
== 0 || !dir_allow_read
)
919 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
,
920 "Transfer done (but failed to open directory).");
924 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Directory send OK.");
930 vsf_sysutil_closedir(p_dir
);
934 port_cleanup(p_sess
);
935 pasv_cleanup(p_sess
);
940 handle_type(struct vsf_session
* p_sess
)
942 str_upper(&p_sess
->ftp_arg_str
);
943 if (str_equal_text(&p_sess
->ftp_arg_str
, "I") ||
944 str_equal_text(&p_sess
->ftp_arg_str
, "L8") ||
945 str_equal_text(&p_sess
->ftp_arg_str
, "L 8"))
947 p_sess
->is_ascii
= 0;
948 vsf_cmdio_write(p_sess
, FTP_TYPEOK
, "Switching to Binary mode.");
950 else if (str_equal_text(&p_sess
->ftp_arg_str
, "A") ||
951 str_equal_text(&p_sess
->ftp_arg_str
, "A N"))
953 p_sess
->is_ascii
= 1;
954 vsf_cmdio_write(p_sess
, FTP_TYPEOK
, "Switching to ASCII mode.");
958 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unrecognised TYPE command.");
963 handle_port(struct vsf_session
* p_sess
)
965 unsigned short the_port
;
966 unsigned char vals
[6];
967 const unsigned char* p_raw
;
968 pasv_cleanup(p_sess
);
969 port_cleanup(p_sess
);
970 p_raw
= vsf_sysutil_parse_uchar_string_sep(&p_sess
->ftp_arg_str
, ',', vals
,
974 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal PORT command.");
977 the_port
= (unsigned short) ((vals
[4] << 8) | vals
[5]);
978 vsf_sysutil_sockaddr_clone(&p_sess
->p_port_sockaddr
, p_sess
->p_local_addr
);
979 vsf_sysutil_sockaddr_set_ipv4addr(p_sess
->p_port_sockaddr
, vals
);
980 vsf_sysutil_sockaddr_set_port(p_sess
->p_port_sockaddr
, the_port
);
982 * 1) Reject requests not connecting to the control socket IP
983 * 2) Reject connects to privileged ports
985 if (!tunable_port_promiscuous
)
987 if (!vsf_sysutil_sockaddr_addr_equal(p_sess
->p_remote_addr
,
988 p_sess
->p_port_sockaddr
) ||
989 vsf_sysutil_is_port_reserved(the_port
))
991 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal PORT command.");
992 port_cleanup(p_sess
);
996 vsf_cmdio_write(p_sess
, FTP_PORTOK
,
997 "PORT command successful. Consider using PASV.");
1001 handle_stor(struct vsf_session
* p_sess
)
1003 handle_upload_common(p_sess
, 0, 0);
1007 handle_upload_common(struct vsf_session
* p_sess
, int is_append
, int is_unique
)
1009 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1010 static struct mystr s_filename
;
1011 struct mystr
* p_filename
;
1012 struct vsf_transfer_ret trans_ret
;
1017 int do_truncate
= 0;
1018 filesize_t offset
= p_sess
->restart_pos
;
1019 p_sess
->restart_pos
= 0;
1020 if (!data_transfer_checks_ok(p_sess
))
1024 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1025 p_filename
= &p_sess
->ftp_arg_str
;
1028 get_unique_filename(&s_filename
, p_filename
);
1029 p_filename
= &s_filename
;
1031 vsf_log_start_entry(p_sess
, kVSFLogEntryUpload
);
1032 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1033 prepend_path_to_filename(&p_sess
->log_str
);
1034 if (!vsf_access_check_file(p_filename
))
1036 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1039 /* NOTE - actual file permissions will be governed by the tunable umask */
1040 /* XXX - do we care about race between create and chown() of anonymous
1043 if (is_unique
|| (p_sess
->is_anonymous
&& !tunable_anon_other_write_enable
))
1045 new_file_fd
= str_create_exclusive(p_filename
);
1049 /* For non-anonymous, allow open() to overwrite or append existing files */
1050 new_file_fd
= str_create(p_filename
);
1051 if (!is_append
&& offset
== 0)
1056 if (vsf_sysutil_retval_is_error(new_file_fd
))
1058 vsf_cmdio_write(p_sess
, FTP_UPLOADFAIL
, "Could not create file.");
1062 vsf_sysutil_fstat(new_file_fd
, &s_p_statbuf
);
1063 if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1065 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
1066 * such as XFS DMAPI.
1068 vsf_sysutil_deactivate_noblock(new_file_fd
);
1070 /* Are we required to chown() this file for security? */
1071 if (p_sess
->is_anonymous
&& tunable_chown_uploads
)
1073 vsf_sysutil_fchmod(new_file_fd
, tunable_chown_upload_mode
);
1074 if (tunable_one_process_model
)
1076 vsf_one_process_chown_upload(p_sess
, new_file_fd
);
1080 vsf_two_process_chown_upload(p_sess
, new_file_fd
);
1083 /* Are we required to lock this file? */
1084 if (tunable_lock_upload_files
)
1086 vsf_sysutil_lock_file_write(new_file_fd
);
1088 /* Must truncate the file AFTER locking it! */
1091 vsf_sysutil_ftruncate(new_file_fd
);
1092 vsf_sysutil_lseek_to(new_file_fd
, 0);
1094 if (!is_append
&& offset
!= 0)
1096 /* XXX - warning, allows seek past end of file! Check for seek > size? */
1097 vsf_sysutil_lseek_to(new_file_fd
, offset
);
1101 vsf_sysutil_lseek_end(new_file_fd
);
1105 struct mystr resp_str
= INIT_MYSTR
;
1106 str_alloc_text(&resp_str
, "FILE: ");
1107 str_append_str(&resp_str
, p_filename
);
1108 remote_fd
= get_remote_transfer_fd(p_sess
, str_getbuf(&resp_str
));
1109 str_free(&resp_str
);
1113 remote_fd
= get_remote_transfer_fd(p_sess
, "Ok to send data.");
1115 if (vsf_sysutil_retval_is_error(remote_fd
))
1117 goto port_pasv_cleanup_out
;
1119 if (tunable_ascii_upload_enable
&& p_sess
->is_ascii
)
1121 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
1126 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
1129 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && trans_ret
.retval
== 0)
1131 trans_ret
.retval
= -2;
1133 p_sess
->transfer_size
= trans_ret
.transferred
;
1134 if (trans_ret
.retval
== 0)
1137 vsf_log_do_log(p_sess
, 1);
1139 if (trans_ret
.retval
== -1)
1141 vsf_cmdio_write(p_sess
, FTP_BADSENDFILE
, "Failure writing to local file.");
1143 else if (trans_ret
.retval
== -2)
1145 if (!p_sess
->data_timeout
)
1147 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
,
1148 "Failure reading network stream.");
1153 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Transfer complete.");
1156 port_pasv_cleanup_out
:
1157 port_cleanup(p_sess
);
1158 pasv_cleanup(p_sess
);
1159 if (tunable_delete_failed_uploads
&& created
&& !success
)
1161 str_unlink(p_filename
);
1163 vsf_sysutil_close(new_file_fd
);
1167 handle_mkd(struct vsf_session
* p_sess
)
1170 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1171 vsf_log_start_entry(p_sess
, kVSFLogEntryMkdir
);
1172 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1173 prepend_path_to_filename(&p_sess
->log_str
);
1174 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1176 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1179 /* NOTE! Actual permissions will be governed by the tunable umask */
1180 retval
= str_mkdir(&p_sess
->ftp_arg_str
, 0777);
1183 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1184 "Create directory operation failed.");
1187 vsf_log_do_log(p_sess
, 1);
1189 static struct mystr s_mkd_res
;
1190 static struct mystr s_tmp_str
;
1191 str_copy(&s_tmp_str
, &p_sess
->ftp_arg_str
);
1192 prepend_path_to_filename(&s_tmp_str
);
1193 /* Double up double quotes */
1194 str_replace_text(&s_tmp_str
, "\"", "\"\"");
1195 /* Build result string */
1196 str_alloc_text(&s_mkd_res
, "\"");
1197 str_append_str(&s_mkd_res
, &s_tmp_str
);
1198 str_append_text(&s_mkd_res
, "\" created");
1199 vsf_cmdio_write_str(p_sess
, FTP_MKDIROK
, &s_mkd_res
);
1204 handle_rmd(struct vsf_session
* p_sess
)
1207 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1208 vsf_log_start_entry(p_sess
, kVSFLogEntryRmdir
);
1209 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1210 prepend_path_to_filename(&p_sess
->log_str
);
1211 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1213 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1216 retval
= str_rmdir(&p_sess
->ftp_arg_str
);
1219 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1220 "Remove directory operation failed.");
1224 vsf_log_do_log(p_sess
, 1);
1225 vsf_cmdio_write(p_sess
, FTP_RMDIROK
,
1226 "Remove directory operation successful.");
1231 handle_dele(struct vsf_session
* p_sess
)
1234 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1235 vsf_log_start_entry(p_sess
, kVSFLogEntryDelete
);
1236 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1237 prepend_path_to_filename(&p_sess
->log_str
);
1238 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1240 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1243 retval
= str_unlink(&p_sess
->ftp_arg_str
);
1246 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Delete operation failed.");
1250 vsf_log_do_log(p_sess
, 1);
1251 vsf_cmdio_write(p_sess
, FTP_DELEOK
, "Delete operation successful.");
1256 handle_rest(struct vsf_session
* p_sess
)
1258 static struct mystr s_rest_str
;
1259 filesize_t val
= str_a_to_filesize_t(&p_sess
->ftp_arg_str
);
1264 p_sess
->restart_pos
= val
;
1265 str_alloc_text(&s_rest_str
, "Restart position accepted (");
1266 str_append_filesize_t(&s_rest_str
, val
);
1267 str_append_text(&s_rest_str
, ").");
1268 vsf_cmdio_write_str(p_sess
, FTP_RESTOK
, &s_rest_str
);
1272 handle_rnfr(struct vsf_session
* p_sess
)
1274 static struct vsf_sysutil_statbuf
* p_statbuf
;
1276 /* Clear old value */
1277 str_free(&p_sess
->rnfr_filename_str
);
1278 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1279 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1281 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1282 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1283 prepend_path_to_filename(&p_sess
->log_str
);
1284 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1287 /* Does it exist? */
1288 retval
= str_stat(&p_sess
->ftp_arg_str
, &p_statbuf
);
1292 str_copy(&p_sess
->rnfr_filename_str
, &p_sess
->ftp_arg_str
);
1293 vsf_cmdio_write(p_sess
, FTP_RNFROK
, "Ready for RNTO.");
1297 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1298 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1299 prepend_path_to_filename(&p_sess
->log_str
);
1300 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "RNFR command failed.");
1305 handle_rnto(struct vsf_session
* p_sess
)
1307 static struct mystr s_tmp_str
;
1309 /* If we didn't get a RNFR, throw a wobbly */
1310 if (str_isempty(&p_sess
->rnfr_filename_str
))
1312 vsf_cmdio_write(p_sess
, FTP_NEEDRNFR
,
1313 "RNFR required first.");
1316 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1317 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1318 str_copy(&p_sess
->log_str
, &p_sess
->rnfr_filename_str
);
1319 prepend_path_to_filename(&p_sess
->log_str
);
1320 str_append_char(&p_sess
->log_str
, ' ');
1321 str_copy(&s_tmp_str
, &p_sess
->ftp_arg_str
);
1322 prepend_path_to_filename(&s_tmp_str
);
1323 str_append_str(&p_sess
->log_str
, &s_tmp_str
);
1324 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1326 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1329 /* NOTE - might overwrite destination file. Not a concern because the same
1330 * could be accomplished with DELE.
1332 retval
= str_rename(&p_sess
->rnfr_filename_str
, &p_sess
->ftp_arg_str
);
1333 /* Clear the RNFR filename; start the two stage process again! */
1334 str_free(&p_sess
->rnfr_filename_str
);
1337 vsf_log_do_log(p_sess
, 1);
1338 vsf_cmdio_write(p_sess
, FTP_RENAMEOK
, "Rename successful.");
1342 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Rename failed.");
1347 handle_nlst(struct vsf_session
* p_sess
)
1349 handle_dir_common(p_sess
, 0, 0);
1353 prepend_path_to_filename(struct mystr
* p_str
)
1355 static struct mystr s_tmp_str
;
1356 /* Only prepend current working directory if the incoming filename is
1359 str_empty(&s_tmp_str
);
1360 if (str_isempty(p_str
) || str_get_char_at(p_str
, 0) != '/')
1362 str_getcwd(&s_tmp_str
);
1363 /* Careful to not emit // if we are in directory / (common with chroot) */
1364 if (str_isempty(&s_tmp_str
) ||
1365 str_get_char_at(&s_tmp_str
, str_getlen(&s_tmp_str
) - 1) != '/')
1367 str_append_char(&s_tmp_str
, '/');
1370 str_append_str(&s_tmp_str
, p_str
);
1371 str_copy(p_str
, &s_tmp_str
);
1376 handle_sigurg(void* p_private
)
1378 struct mystr async_cmd_str
= INIT_MYSTR
;
1379 struct mystr async_arg_str
= INIT_MYSTR
;
1380 struct mystr real_cmd_str
= INIT_MYSTR
;
1382 struct vsf_session
* p_sess
= (struct vsf_session
*) p_private
;
1383 /* Did stupid client sent something OOB without a data connection? */
1384 if (p_sess
->data_fd
== -1)
1388 /* Get the async command - blocks (use data timeout alarm) */
1389 vsf_cmdio_get_cmd_and_arg(p_sess
, &async_cmd_str
, &async_arg_str
, 0);
1390 /* Chop off first four characters; they are telnet characters. The client
1391 * should have sent the first two normally and the second two as urgent
1394 len
= str_getlen(&async_cmd_str
);
1397 str_right(&async_cmd_str
, &real_cmd_str
, len
- 4);
1399 if (str_equal_text(&real_cmd_str
, "ABOR"))
1401 p_sess
->abor_received
= 1;
1402 /* This is failok because of a small race condition; the SIGURG might
1403 * be raised after the data socket is closed, but before data_fd is
1406 vsf_sysutil_shutdown_failok(p_sess
->data_fd
);
1411 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown command.");
1413 str_free(&async_cmd_str
);
1414 str_free(&async_arg_str
);
1415 str_free(&real_cmd_str
);
1419 get_remote_transfer_fd(struct vsf_session
* p_sess
, const char* p_status_msg
)
1422 if (!pasv_active(p_sess
) && !port_active(p_sess
))
1424 bug("neither PORT nor PASV active in get_remote_transfer_fd");
1426 p_sess
->abor_received
= 0;
1427 if (pasv_active(p_sess
))
1429 remote_fd
= vsf_ftpdataio_get_pasv_fd(p_sess
);
1433 remote_fd
= vsf_ftpdataio_get_port_fd(p_sess
);
1435 if (vsf_sysutil_retval_is_error(remote_fd
))
1439 vsf_cmdio_write(p_sess
, FTP_DATACONN
, p_status_msg
);
1440 if (vsf_ftpdataio_post_mark_connect(p_sess
) != 1)
1442 vsf_ftpdataio_dispose_transfer_fd(p_sess
);
1449 check_abor(struct vsf_session
* p_sess
)
1451 /* If the client sent ABOR, respond to it here */
1452 if (p_sess
->abor_received
)
1454 p_sess
->abor_received
= 0;
1455 vsf_cmdio_write(p_sess
, FTP_ABOROK
, "ABOR successful.");
1460 handle_size(struct vsf_session
* p_sess
)
1462 /* Note - in ASCII mode, are supposed to return the size after taking into
1463 * account ASCII linefeed conversions. At least this is what wu-ftpd does in
1464 * version 2.6.1. Proftpd-1.2.0pre fails to do this.
1465 * I will not do it because it is a potential I/O DoS.
1467 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1469 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1470 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1472 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1475 retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1476 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1478 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Could not get file size.");
1482 static struct mystr s_size_res_str
;
1483 str_alloc_filesize_t(&s_size_res_str
,
1484 vsf_sysutil_statbuf_get_size(s_p_statbuf
));
1485 vsf_cmdio_write_str(p_sess
, FTP_SIZEOK
, &s_size_res_str
);
1490 handle_site(struct vsf_session
* p_sess
)
1492 static struct mystr s_site_args_str
;
1493 /* What SITE sub-command is it? */
1494 str_split_char(&p_sess
->ftp_arg_str
, &s_site_args_str
, ' ');
1495 str_upper(&p_sess
->ftp_arg_str
);
1496 if (tunable_write_enable
&&
1497 tunable_chmod_enable
&&
1498 str_equal_text(&p_sess
->ftp_arg_str
, "CHMOD"))
1500 handle_site_chmod(p_sess
, &s_site_args_str
);
1502 else if (str_equal_text(&p_sess
->ftp_arg_str
, "UMASK"))
1504 handle_site_umask(p_sess
, &s_site_args_str
);
1506 else if (str_equal_text(&p_sess
->ftp_arg_str
, "HELP"))
1508 if (tunable_write_enable
&&
1509 tunable_chmod_enable
)
1511 vsf_cmdio_write(p_sess
, FTP_SITEHELP
, "CHMOD UMASK HELP");
1515 vsf_cmdio_write(p_sess
, FTP_SITEHELP
, "UMASK HELP");
1520 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown SITE command.");
1525 handle_site_chmod(struct vsf_session
* p_sess
, struct mystr
* p_arg_str
)
1527 static struct mystr s_chmod_file_str
;
1530 if (str_isempty(p_arg_str
))
1532 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "SITE CHMOD needs 2 arguments.");
1535 str_split_char(p_arg_str
, &s_chmod_file_str
, ' ');
1536 if (str_isempty(&s_chmod_file_str
))
1538 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "SITE CHMOD needs 2 arguments.");
1541 resolve_tilde(&s_chmod_file_str
, p_sess
);
1542 vsf_log_start_entry(p_sess
, kVSFLogEntryChmod
);
1543 str_copy(&p_sess
->log_str
, &s_chmod_file_str
);
1544 prepend_path_to_filename(&p_sess
->log_str
);
1545 str_append_char(&p_sess
->log_str
, ' ');
1546 str_append_str(&p_sess
->log_str
, p_arg_str
);
1547 if (!vsf_access_check_file(&s_chmod_file_str
))
1549 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1552 /* Don't worry - our chmod() implementation only allows 0 - 0777 */
1553 perms
= str_octal_to_uint(p_arg_str
);
1554 retval
= str_chmod(&s_chmod_file_str
, perms
);
1555 if (vsf_sysutil_retval_is_error(retval
))
1557 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "SITE CHMOD command failed.");
1561 vsf_log_do_log(p_sess
, 1);
1562 vsf_cmdio_write(p_sess
, FTP_CHMODOK
, "SITE CHMOD command ok.");
1567 handle_site_umask(struct vsf_session
* p_sess
, struct mystr
* p_arg_str
)
1569 static struct mystr s_umask_resp_str
;
1570 if (str_isempty(p_arg_str
))
1572 /* Empty arg => report current umask */
1573 str_alloc_text(&s_umask_resp_str
, "Your current UMASK is ");
1574 str_append_text(&s_umask_resp_str
,
1575 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1579 /* Set current umask */
1580 unsigned int new_umask
= str_octal_to_uint(p_arg_str
);
1581 vsf_sysutil_set_umask(new_umask
);
1582 str_alloc_text(&s_umask_resp_str
, "UMASK set to ");
1583 str_append_text(&s_umask_resp_str
,
1584 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1586 vsf_cmdio_write_str(p_sess
, FTP_UMASKOK
, &s_umask_resp_str
);
1590 handle_appe(struct vsf_session
* p_sess
)
1592 handle_upload_common(p_sess
, 1, 0);
1596 handle_mdtm(struct vsf_session
* p_sess
)
1598 static struct mystr s_filename_str
;
1599 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1602 struct str_locate_result loc
= str_locate_char(&p_sess
->ftp_arg_str
, ' ');
1603 int retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1604 if (tunable_mdtm_write
&& retval
!= 0 && loc
.found
&&
1605 vsf_sysutil_isdigit(str_get_char_at(&p_sess
->ftp_arg_str
, 0)))
1607 if (loc
.index
== 8 || loc
.index
== 14 ||
1608 (loc
.index
> 15 && str_get_char_at(&p_sess
->ftp_arg_str
, 14) == '.'))
1615 str_split_char(&p_sess
->ftp_arg_str
, &s_filename_str
, ' ');
1616 modtime
= vsf_sysutil_parse_time(str_getbuf(&p_sess
->ftp_arg_str
));
1617 str_copy(&p_sess
->ftp_arg_str
, &s_filename_str
);
1619 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1620 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1622 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1625 if (do_write
&& tunable_write_enable
&&
1626 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
))
1628 retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1629 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1631 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1632 "Could not set file modification time.");
1636 retval
= vsf_sysutil_setmodtime(
1637 str_getbuf(&p_sess
->ftp_arg_str
), modtime
, tunable_use_localtime
);
1640 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1641 "Could not set file modification time.");
1645 vsf_cmdio_write(p_sess
, FTP_MDTMOK
,
1646 "File modification time set.");
1652 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1654 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1655 "Could not get file modification time.");
1659 static struct mystr s_mdtm_res_str
;
1660 str_alloc_text(&s_mdtm_res_str
,
1661 vsf_sysutil_statbuf_get_numeric_date(
1662 s_p_statbuf
, tunable_use_localtime
));
1663 vsf_cmdio_write_str(p_sess
, FTP_MDTMOK
, &s_mdtm_res_str
);
1669 handle_eprt(struct vsf_session
* p_sess
)
1671 static struct mystr s_part1_str
;
1672 static struct mystr s_part2_str
;
1673 static struct mystr s_scopeid_str
;
1676 const unsigned char* p_raw_addr
;
1677 int is_ipv6
= vsf_sysutil_sockaddr_is_ipv6(p_sess
->p_local_addr
);
1678 port_cleanup(p_sess
);
1679 pasv_cleanup(p_sess
);
1680 str_copy(&s_part1_str
, &p_sess
->ftp_arg_str
);
1681 str_split_char(&s_part1_str
, &s_part2_str
, '|');
1682 if (!str_isempty(&s_part1_str
))
1686 /* Split out the protocol and check it */
1687 str_split_char(&s_part2_str
, &s_part1_str
, '|');
1688 proto
= str_atoi(&s_part2_str
);
1689 if (proto
< 1 || proto
> 2 || (!is_ipv6
&& proto
== 2))
1691 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Bad EPRT protocol.");
1694 /* Split out address and parse it */
1695 str_split_char(&s_part1_str
, &s_part2_str
, '|');
1698 str_split_char(&s_part1_str
, &s_scopeid_str
, '%');
1699 p_raw_addr
= vsf_sysutil_parse_ipv6(&s_part1_str
);
1703 p_raw_addr
= vsf_sysutil_parse_ipv4(&s_part1_str
);
1709 /* Split out port and parse it */
1710 str_split_char(&s_part2_str
, &s_part1_str
, '|');
1711 if (!str_isempty(&s_part1_str
) || str_isempty(&s_part2_str
))
1715 port
= str_atoi(&s_part2_str
);
1716 if (port
< 0 || port
> 65535)
1720 vsf_sysutil_sockaddr_clone(&p_sess
->p_port_sockaddr
, p_sess
->p_local_addr
);
1723 vsf_sysutil_sockaddr_set_ipv6addr(p_sess
->p_port_sockaddr
, p_raw_addr
);
1727 vsf_sysutil_sockaddr_set_ipv4addr(p_sess
->p_port_sockaddr
, p_raw_addr
);
1729 vsf_sysutil_sockaddr_set_port(p_sess
->p_port_sockaddr
, (unsigned short) port
);
1731 * 1) Reject requests not connecting to the control socket IP
1732 * 2) Reject connects to privileged ports
1734 if (!tunable_port_promiscuous
)
1736 if (!vsf_sysutil_sockaddr_addr_equal(p_sess
->p_remote_addr
,
1737 p_sess
->p_port_sockaddr
) ||
1738 vsf_sysutil_is_port_reserved((unsigned short) port
))
1740 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal EPRT command.");
1741 port_cleanup(p_sess
);
1745 vsf_cmdio_write(p_sess
, FTP_EPRTOK
,
1746 "EPRT command successful. Consider using EPSV.");
1749 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Bad EPRT command.");
1752 /* XXX - add AUTH etc. */
1754 handle_help(struct vsf_session
* p_sess
)
1756 vsf_cmdio_write_hyphen(p_sess
, FTP_HELP
,
1757 "The following commands are recognized.");
1758 vsf_cmdio_write_raw(p_sess
,
1759 " ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n");
1760 vsf_cmdio_write_raw(p_sess
,
1761 " MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n");
1762 vsf_cmdio_write_raw(p_sess
,
1763 " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
1764 vsf_cmdio_write_raw(p_sess
,
1766 vsf_cmdio_write(p_sess
, FTP_HELP
, "Help OK.");
1770 handle_stou(struct vsf_session
* p_sess
)
1772 handle_upload_common(p_sess
, 0, 1);
1776 get_unique_filename(struct mystr
* p_outstr
, const struct mystr
* p_base_str
)
1778 /* Use silly wu-ftpd algorithm for compatibility. It has races of course, if
1779 * two sessions are using the same file prefix at the same time.
1781 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1782 static struct mystr s_stou_str
;
1783 unsigned int suffix
= 1;
1784 const struct mystr
* p_real_base_str
= p_base_str
;
1786 if (str_isempty(p_real_base_str
))
1788 str_alloc_text(&s_stou_str
, "STOU");
1789 p_real_base_str
= &s_stou_str
;
1793 /* Do not add any suffix at all if the name is not taken. */
1794 retval
= str_stat(p_real_base_str
, &s_p_statbuf
);
1795 if (vsf_sysutil_retval_is_error(retval
))
1797 str_copy(p_outstr
, p_real_base_str
);
1803 str_copy(p_outstr
, p_real_base_str
);
1804 str_append_char(p_outstr
, '.');
1805 str_append_ulong(p_outstr
, suffix
);
1806 retval
= str_stat(p_outstr
, &s_p_statbuf
);
1807 if (vsf_sysutil_retval_is_error(retval
))
1816 handle_stat(struct vsf_session
* p_sess
)
1818 vsf_cmdio_write_hyphen(p_sess
, FTP_STATOK
, "FTP server status:");
1819 vsf_cmdio_write_raw(p_sess
, " Connected to ");
1820 vsf_cmdio_write_raw(p_sess
, str_getbuf(&p_sess
->remote_ip_str
));
1821 vsf_cmdio_write_raw(p_sess
, "\r\n");
1822 vsf_cmdio_write_raw(p_sess
, " Logged in as ");
1823 vsf_cmdio_write_raw(p_sess
, str_getbuf(&p_sess
->user_str
));
1824 vsf_cmdio_write_raw(p_sess
, "\r\n");
1825 vsf_cmdio_write_raw(p_sess
, " TYPE: ");
1826 if (p_sess
->is_ascii
)
1828 vsf_cmdio_write_raw(p_sess
, "ASCII\r\n");
1832 vsf_cmdio_write_raw(p_sess
, "BINARY\r\n");
1834 if (p_sess
->bw_rate_max
== 0)
1836 vsf_cmdio_write_raw(p_sess
, " No session bandwidth limit\r\n");
1840 vsf_cmdio_write_raw(p_sess
, " Session bandwidth limit in byte/s is ");
1841 vsf_cmdio_write_raw(p_sess
, vsf_sysutil_ulong_to_str(p_sess
->bw_rate_max
));
1842 vsf_cmdio_write_raw(p_sess
, "\r\n");
1844 if (tunable_idle_session_timeout
== 0)
1846 vsf_cmdio_write_raw(p_sess
, " No session timeout\r\n");
1850 vsf_cmdio_write_raw(p_sess
, " Session timeout in seconds is ");
1851 vsf_cmdio_write_raw(p_sess
,
1852 vsf_sysutil_ulong_to_str(tunable_idle_session_timeout
));
1853 vsf_cmdio_write_raw(p_sess
, "\r\n");
1855 if (p_sess
->control_use_ssl
)
1857 vsf_cmdio_write_raw(p_sess
, " Control connection is encrypted\r\n");
1861 vsf_cmdio_write_raw(p_sess
, " Control connection is plain text\r\n");
1863 if (p_sess
->data_use_ssl
)
1865 vsf_cmdio_write_raw(p_sess
, " Data connections will be encrypted\r\n");
1869 vsf_cmdio_write_raw(p_sess
, " Data connections will be plain text\r\n");
1871 if (p_sess
->num_clients
> 0)
1873 vsf_cmdio_write_raw(p_sess
, " At session startup, client count was ");
1874 vsf_cmdio_write_raw(p_sess
, vsf_sysutil_ulong_to_str(p_sess
->num_clients
));
1875 vsf_cmdio_write_raw(p_sess
, "\r\n");
1877 vsf_cmdio_write_raw(p_sess
,
1878 " vsFTPd " VSF_VERSION
" - secure, fast, stable\r\n");
1879 vsf_cmdio_write(p_sess
, FTP_STATOK
, "End of status");
1883 handle_stat_file(struct vsf_session
* p_sess
)
1885 handle_dir_common(p_sess
, 1, 1);
1889 data_transfer_checks_ok(struct vsf_session
* p_sess
)
1891 if (!pasv_active(p_sess
) && !port_active(p_sess
))
1893 vsf_cmdio_write(p_sess
, FTP_BADSENDCONN
, "Use PORT or PASV first.");
1896 if (tunable_ssl_enable
&& !p_sess
->data_use_ssl
&&
1897 ((tunable_force_local_data_ssl
&& !p_sess
->is_anonymous
) ||
1898 (tunable_force_anon_data_ssl
&& p_sess
->is_anonymous
)))
1901 p_sess
, FTP_NEEDENCRYPT
, "Data connections must be encrypted.");
1908 resolve_tilde(struct mystr
* p_str
, struct vsf_session
* p_sess
)
1910 unsigned int len
= str_getlen(p_str
);
1911 if (len
> 0 && str_get_char_at(p_str
, 0) == '~')
1913 static struct mystr s_rhs_str
;
1914 if (len
== 1 || str_get_char_at(p_str
, 1) == '/')
1916 str_split_char(p_str
, &s_rhs_str
, '~');
1917 str_copy(p_str
, &p_sess
->home_str
);
1918 str_append_str(p_str
, &s_rhs_str
);
1920 else if (tunable_tilde_user_enable
&& len
> 1)
1922 static struct mystr s_user_str
;
1923 struct vsf_sysutil_user
* p_user
;
1924 str_copy(&s_rhs_str
, p_str
);
1925 str_split_char(&s_rhs_str
, &s_user_str
, '~');
1926 str_split_char(&s_user_str
, &s_rhs_str
, '/');
1927 p_user
= str_getpwnam(&s_user_str
);
1930 str_alloc_text(p_str
, vsf_sysutil_user_get_homedir(p_user
));
1931 if (!str_isempty(&s_rhs_str
))
1933 str_append_char(p_str
, '/');
1934 str_append_str(p_str
, &s_rhs_str
);
1941 static void handle_logged_in_user(struct vsf_session
* p_sess
)
1943 if (p_sess
->is_anonymous
)
1945 vsf_cmdio_write(p_sess
, FTP_LOGINERR
, "Can't change from guest user.");
1947 else if (str_equal(&p_sess
->user_str
, &p_sess
->ftp_arg_str
))
1949 vsf_cmdio_write(p_sess
, FTP_GIVEPWORD
, "Any password will do.");
1953 vsf_cmdio_write(p_sess
, FTP_LOGINERR
, "Can't change to another user.");
1957 static void handle_logged_in_pass(struct vsf_session
* p_sess
)
1959 vsf_cmdio_write(p_sess
, FTP_LOGINOK
, "Already logged in.");
1963 handle_http(struct vsf_session
* p_sess
)
1965 /* Warning: Doesn't respect cmds_allowed etc. because there is currently only
1966 * one command (GET)!
1967 * HTTP likely doesn't respect other important FTP options. I don't think
1970 if (!tunable_download_enable
)
1972 bug("HTTP needs download - fix your config");
1974 /* Eat the HTTP headers, which we don't care about. */
1977 vsf_cmdio_get_cmd_and_arg(p_sess
, &p_sess
->ftp_cmd_str
,
1978 &p_sess
->ftp_arg_str
, 1);
1980 while (!str_isempty(&p_sess
->ftp_cmd_str
) ||
1981 !str_isempty(&p_sess
->ftp_arg_str
));
1982 vsf_cmdio_write_raw(p_sess
, "HTTP/1.1 200 OK\r\n");
1983 vsf_cmdio_write_raw(p_sess
, "Server: vsftpd\r\n");
1984 vsf_cmdio_write_raw(p_sess
, "Connection: close\r\n");
1985 vsf_cmdio_write_raw(p_sess
, "X-Frame-Options: SAMEORIGIN\r\n");
1986 vsf_cmdio_write_raw(p_sess
, "X-Content-Type-Options: nosniff\r\n");
1987 /* Split the path from the HTTP/1.x */
1988 str_split_char(&p_sess
->http_get_arg
, &p_sess
->ftp_arg_str
, ' ');
1989 str_copy(&p_sess
->ftp_arg_str
, &p_sess
->http_get_arg
);
1990 str_split_char(&p_sess
->http_get_arg
, &p_sess
->ftp_cmd_str
, '.');
1991 str_upper(&p_sess
->ftp_cmd_str
);
1992 if (str_equal_text(&p_sess
->ftp_cmd_str
, "HTML") ||
1993 str_equal_text(&p_sess
->ftp_cmd_str
, "HTM"))
1995 vsf_cmdio_write_raw(p_sess
, "Content-Type: text/html\r\n");
1999 vsf_cmdio_write_raw(p_sess
, "Content-Type: dunno\r\n");
2001 vsf_cmdio_write_raw(p_sess
, "\r\n");
2002 p_sess
->is_ascii
= 0;
2003 p_sess
->restart_pos
= 0;
2004 handle_retr(p_sess
, 1);
2005 if (vsf_log_entry_pending(p_sess
))
2007 vsf_log_do_log(p_sess
, 0);
2009 vsf_sysutil_exit(0);