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 if (tunable_async_abor_enable
)
105 vsf_sysutil_install_sighandler(kVSFSysUtilSigURG
, handle_sigurg
, p_sess
, 0);
106 vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD
);
108 /* Handle any login message */
109 vsf_banner_dir_changed(p_sess
, FTP_LOGINOK
);
110 vsf_cmdio_write(p_sess
, FTP_LOGINOK
, "Login successful.");
115 if (tunable_setproctitle_enable
)
117 vsf_sysutil_setproctitle("IDLE");
120 vsf_cmdio_get_cmd_and_arg(p_sess
, &p_sess
->ftp_cmd_str
,
121 &p_sess
->ftp_arg_str
, 1);
122 if (tunable_setproctitle_enable
)
124 struct mystr proctitle_str
= INIT_MYSTR
;
125 str_copy(&proctitle_str
, &p_sess
->ftp_cmd_str
);
126 if (!str_isempty(&p_sess
->ftp_arg_str
))
128 str_append_char(&proctitle_str
, ' ');
129 str_append_str(&proctitle_str
, &p_sess
->ftp_arg_str
);
131 /* Suggestion from Solar */
132 str_replace_unprintable(&proctitle_str
, '?');
133 vsf_sysutil_setproctitle_str(&proctitle_str
);
134 str_free(&proctitle_str
);
136 /* Test command against the allowed lists.. */
137 if (tunable_cmds_allowed
)
139 static struct mystr s_src_str
;
140 static struct mystr s_rhs_str
;
141 str_alloc_text(&s_src_str
, tunable_cmds_allowed
);
144 str_split_char(&s_src_str
, &s_rhs_str
, ',');
145 if (str_isempty(&s_src_str
))
150 else if (str_equal(&s_src_str
, &p_sess
->ftp_cmd_str
))
154 str_copy(&s_src_str
, &s_rhs_str
);
157 if (tunable_cmds_denied
)
159 static struct mystr s_src_str
;
160 static struct mystr s_rhs_str
;
161 str_alloc_text(&s_src_str
, tunable_cmds_denied
);
164 str_split_char(&s_src_str
, &s_rhs_str
, ',');
165 if (str_isempty(&s_src_str
))
169 else if (str_equal(&s_src_str
, &p_sess
->ftp_cmd_str
))
174 str_copy(&s_src_str
, &s_rhs_str
);
179 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
181 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "QUIT"))
183 vsf_cmdio_write_exit(p_sess
, FTP_GOODBYE
, "Goodbye.");
185 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PWD") ||
186 str_equal_text(&p_sess
->ftp_cmd_str
, "XPWD"))
190 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "CWD") ||
191 str_equal_text(&p_sess
->ftp_cmd_str
, "XCWD"))
195 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "CDUP") ||
196 str_equal_text(&p_sess
->ftp_cmd_str
, "XCUP"))
200 else if (tunable_pasv_enable
&&
202 (str_equal_text(&p_sess
->ftp_cmd_str
, "PASV") ||
203 str_equal_text(&p_sess
->ftp_cmd_str
, "P@SW")))
205 handle_pasv(p_sess
, 0);
207 else if (tunable_pasv_enable
&&
208 str_equal_text(&p_sess
->ftp_cmd_str
, "EPSV"))
210 handle_pasv(p_sess
, 1);
212 else if (tunable_download_enable
&&
213 str_equal_text(&p_sess
->ftp_cmd_str
, "RETR"))
215 handle_retr(p_sess
, 0);
217 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "NOOP"))
219 vsf_cmdio_write(p_sess
, FTP_NOOPOK
, "NOOP ok.");
221 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SYST"))
223 vsf_cmdio_write(p_sess
, FTP_SYSTOK
, "UNIX Type: L8");
225 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "HELP"))
229 else if (tunable_dirlist_enable
&&
230 str_equal_text(&p_sess
->ftp_cmd_str
, "LIST"))
234 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "TYPE"))
238 else if (tunable_port_enable
&&
240 str_equal_text(&p_sess
->ftp_cmd_str
, "PORT"))
244 else if (tunable_write_enable
&&
245 (tunable_anon_upload_enable
|| !p_sess
->is_anonymous
) &&
246 str_equal_text(&p_sess
->ftp_cmd_str
, "STOR"))
250 else if (tunable_write_enable
&&
251 (tunable_anon_mkdir_write_enable
|| !p_sess
->is_anonymous
) &&
252 (str_equal_text(&p_sess
->ftp_cmd_str
, "MKD") ||
253 str_equal_text(&p_sess
->ftp_cmd_str
, "XMKD")))
257 else if (tunable_write_enable
&&
258 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
259 (str_equal_text(&p_sess
->ftp_cmd_str
, "RMD") ||
260 str_equal_text(&p_sess
->ftp_cmd_str
, "XRMD")))
264 else if (tunable_write_enable
&&
265 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
266 str_equal_text(&p_sess
->ftp_cmd_str
, "DELE"))
270 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "REST"))
274 else if (tunable_write_enable
&&
275 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
276 str_equal_text(&p_sess
->ftp_cmd_str
, "RNFR"))
280 else if (tunable_write_enable
&&
281 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
282 str_equal_text(&p_sess
->ftp_cmd_str
, "RNTO"))
286 else if (tunable_dirlist_enable
&&
287 str_equal_text(&p_sess
->ftp_cmd_str
, "NLST"))
291 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SIZE"))
295 else if (!p_sess
->is_anonymous
&&
296 str_equal_text(&p_sess
->ftp_cmd_str
, "SITE"))
300 /* Note - the weird ABOR string is checking for an async ABOR arriving
301 * without a SIGURG condition.
303 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ABOR") ||
304 str_equal_text(&p_sess
->ftp_cmd_str
, "\377\364\377\362ABOR"))
306 vsf_cmdio_write(p_sess
, FTP_ABOR_NOCONN
, "No transfer to ABOR.");
308 else if (tunable_write_enable
&&
309 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
310 str_equal_text(&p_sess
->ftp_cmd_str
, "APPE"))
314 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "MDTM"))
318 else if (tunable_port_enable
&&
319 str_equal_text(&p_sess
->ftp_cmd_str
, "EPRT"))
323 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "STRU"))
325 str_upper(&p_sess
->ftp_arg_str
);
326 if (str_equal_text(&p_sess
->ftp_arg_str
, "F"))
328 vsf_cmdio_write(p_sess
, FTP_STRUOK
, "Structure set to F.");
332 vsf_cmdio_write(p_sess
, FTP_BADSTRU
, "Bad STRU command.");
335 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "MODE"))
337 str_upper(&p_sess
->ftp_arg_str
);
338 if (str_equal_text(&p_sess
->ftp_arg_str
, "S"))
340 vsf_cmdio_write(p_sess
, FTP_MODEOK
, "Mode set to S.");
344 vsf_cmdio_write(p_sess
, FTP_BADMODE
, "Bad MODE command.");
347 else if (tunable_write_enable
&&
348 (tunable_anon_upload_enable
|| !p_sess
->is_anonymous
) &&
349 str_equal_text(&p_sess
->ftp_cmd_str
, "STOU"))
353 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ALLO"))
355 vsf_cmdio_write(p_sess
, FTP_ALLOOK
, "ALLO command ignored.");
357 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "REIN"))
359 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "REIN not implemented.");
361 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ACCT"))
363 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "ACCT not implemented.");
365 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SMNT"))
367 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "SMNT not implemented.");
369 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "FEAT"))
373 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "OPTS"))
377 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "STAT") &&
378 str_isempty(&p_sess
->ftp_arg_str
))
382 else if (tunable_dirlist_enable
&&
383 str_equal_text(&p_sess
->ftp_cmd_str
, "STAT"))
385 handle_stat_file(p_sess
);
387 else if (tunable_ssl_enable
&& str_equal_text(&p_sess
->ftp_cmd_str
, "PBSZ"))
391 else if (tunable_ssl_enable
&& str_equal_text(&p_sess
->ftp_cmd_str
, "PROT"))
395 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "USER"))
397 handle_logged_in_user(p_sess
);
399 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PASS"))
401 handle_logged_in_pass(p_sess
);
403 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PASV") ||
404 str_equal_text(&p_sess
->ftp_cmd_str
, "PORT") ||
405 str_equal_text(&p_sess
->ftp_cmd_str
, "STOR") ||
406 str_equal_text(&p_sess
->ftp_cmd_str
, "MKD") ||
407 str_equal_text(&p_sess
->ftp_cmd_str
, "XMKD") ||
408 str_equal_text(&p_sess
->ftp_cmd_str
, "RMD") ||
409 str_equal_text(&p_sess
->ftp_cmd_str
, "XRMD") ||
410 str_equal_text(&p_sess
->ftp_cmd_str
, "DELE") ||
411 str_equal_text(&p_sess
->ftp_cmd_str
, "RNFR") ||
412 str_equal_text(&p_sess
->ftp_cmd_str
, "RNTO") ||
413 str_equal_text(&p_sess
->ftp_cmd_str
, "SITE") ||
414 str_equal_text(&p_sess
->ftp_cmd_str
, "APPE") ||
415 str_equal_text(&p_sess
->ftp_cmd_str
, "EPSV") ||
416 str_equal_text(&p_sess
->ftp_cmd_str
, "EPRT") ||
417 str_equal_text(&p_sess
->ftp_cmd_str
, "RETR") ||
418 str_equal_text(&p_sess
->ftp_cmd_str
, "LIST") ||
419 str_equal_text(&p_sess
->ftp_cmd_str
, "NLST") ||
420 str_equal_text(&p_sess
->ftp_cmd_str
, "STOU") ||
421 str_equal_text(&p_sess
->ftp_cmd_str
, "ALLO") ||
422 str_equal_text(&p_sess
->ftp_cmd_str
, "REIN") ||
423 str_equal_text(&p_sess
->ftp_cmd_str
, "ACCT") ||
424 str_equal_text(&p_sess
->ftp_cmd_str
, "SMNT") ||
425 str_equal_text(&p_sess
->ftp_cmd_str
, "FEAT") ||
426 str_equal_text(&p_sess
->ftp_cmd_str
, "OPTS") ||
427 str_equal_text(&p_sess
->ftp_cmd_str
, "STAT") ||
428 str_equal_text(&p_sess
->ftp_cmd_str
, "PBSZ") ||
429 str_equal_text(&p_sess
->ftp_cmd_str
, "PROT"))
431 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
433 else if (str_isempty(&p_sess
->ftp_cmd_str
) &&
434 str_isempty(&p_sess
->ftp_arg_str
))
436 /* Deliberately ignore to avoid NAT device bugs. ProFTPd does the same. */
440 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown command.");
442 if (vsf_log_entry_pending(p_sess
))
444 vsf_log_do_log(p_sess
, 0);
450 handle_pwd(struct vsf_session
* p_sess
)
452 static struct mystr s_cwd_buf_mangle_str
;
453 static struct mystr s_pwd_res_str
;
454 str_getcwd(&s_cwd_buf_mangle_str
);
455 /* Double up any double-quotes in the pathname! */
456 str_replace_text(&s_cwd_buf_mangle_str
, "\"", "\"\"");
457 /* Enclose pathname in quotes */
458 str_alloc_text(&s_pwd_res_str
, "\"");
459 str_append_str(&s_pwd_res_str
, &s_cwd_buf_mangle_str
);
460 str_append_text(&s_pwd_res_str
, "\"");
461 vsf_cmdio_write_str(p_sess
, FTP_PWDOK
, &s_pwd_res_str
);
465 handle_cwd(struct vsf_session
* p_sess
)
468 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
469 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
471 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
474 retval
= str_chdir(&p_sess
->ftp_arg_str
);
477 /* Handle any messages */
478 vsf_banner_dir_changed(p_sess
, FTP_CWDOK
);
479 vsf_cmdio_write(p_sess
, FTP_CWDOK
, "Directory successfully changed.");
483 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to change directory.");
488 handle_cdup(struct vsf_session
* p_sess
)
490 str_alloc_text(&p_sess
->ftp_arg_str
, "..");
495 port_active(struct vsf_session
* p_sess
)
498 if (p_sess
->p_port_sockaddr
!= 0)
501 if (pasv_active(p_sess
))
503 bug("port and pasv both active");
510 pasv_active(struct vsf_session
* p_sess
)
513 if (tunable_one_process_model
)
515 ret
= vsf_one_process_pasv_active(p_sess
);
519 ret
= vsf_two_process_pasv_active(p_sess
);
523 if (port_active(p_sess
))
525 bug("pasv and port both active");
532 port_cleanup(struct vsf_session
* p_sess
)
534 vsf_sysutil_sockaddr_clear(&p_sess
->p_port_sockaddr
);
538 pasv_cleanup(struct vsf_session
* p_sess
)
540 if (tunable_one_process_model
)
542 vsf_one_process_pasv_cleanup(p_sess
);
546 vsf_two_process_pasv_cleanup(p_sess
);
551 handle_pasv(struct vsf_session
* p_sess
, int is_epsv
)
553 unsigned short the_port
;
554 static struct mystr s_pasv_res_str
;
555 static struct vsf_sysutil_sockaddr
* s_p_sockaddr
;
556 int is_ipv6
= vsf_sysutil_sockaddr_is_ipv6(p_sess
->p_local_addr
);
557 if (is_epsv
&& !str_isempty(&p_sess
->ftp_arg_str
))
560 str_upper(&p_sess
->ftp_arg_str
);
561 if (str_equal_text(&p_sess
->ftp_arg_str
, "ALL"))
563 p_sess
->epsv_all
= 1;
564 vsf_cmdio_write(p_sess
, FTP_EPSVALLOK
, "EPSV ALL ok.");
567 argval
= vsf_sysutil_atoi(str_getbuf(&p_sess
->ftp_arg_str
));
568 if (argval
< 1 || argval
> 2 || (!is_ipv6
&& argval
== 2))
570 vsf_cmdio_write(p_sess
, FTP_EPSVBAD
, "Bad network protocol.");
574 pasv_cleanup(p_sess
);
575 port_cleanup(p_sess
);
576 if (tunable_one_process_model
)
578 the_port
= vsf_one_process_listen(p_sess
);
582 the_port
= vsf_two_process_listen(p_sess
);
586 str_alloc_text(&s_pasv_res_str
, "Entering Extended Passive Mode (|||");
587 str_append_ulong(&s_pasv_res_str
, (unsigned long) the_port
);
588 str_append_text(&s_pasv_res_str
, "|).");
589 vsf_cmdio_write_str(p_sess
, FTP_EPSVOK
, &s_pasv_res_str
);
592 if (tunable_pasv_address
!= 0)
594 vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr
);
595 /* Report passive address as specified in configuration */
596 if (vsf_sysutil_inet_aton(tunable_pasv_address
, s_p_sockaddr
) == 0)
598 die("invalid pasv_address");
603 vsf_sysutil_sockaddr_clone(&s_p_sockaddr
, p_sess
->p_local_addr
);
605 str_alloc_text(&s_pasv_res_str
, "Entering Passive Mode (");
608 str_append_text(&s_pasv_res_str
, vsf_sysutil_inet_ntop(s_p_sockaddr
));
612 const void* p_v4addr
= vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr
);
615 str_append_text(&s_pasv_res_str
, vsf_sysutil_inet_ntoa(p_v4addr
));
619 str_append_text(&s_pasv_res_str
, "0,0,0,0");
622 str_replace_char(&s_pasv_res_str
, '.', ',');
623 str_append_text(&s_pasv_res_str
, ",");
624 str_append_ulong(&s_pasv_res_str
, the_port
>> 8);
625 str_append_text(&s_pasv_res_str
, ",");
626 str_append_ulong(&s_pasv_res_str
, the_port
& 255);
627 str_append_text(&s_pasv_res_str
, ").");
628 vsf_cmdio_write_str(p_sess
, FTP_PASVOK
, &s_pasv_res_str
);
632 handle_retr(struct vsf_session
* p_sess
, int is_http
)
634 static struct mystr s_mark_str
;
635 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
636 struct vsf_transfer_ret trans_ret
;
640 filesize_t offset
= p_sess
->restart_pos
;
641 p_sess
->restart_pos
= 0;
642 if (!is_http
&& !data_transfer_checks_ok(p_sess
))
646 if (p_sess
->is_ascii
&& offset
!= 0)
648 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
649 "No support for resume of ASCII transfer.");
652 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
653 vsf_log_start_entry(p_sess
, kVSFLogEntryDownload
);
654 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
655 prepend_path_to_filename(&p_sess
->log_str
);
656 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
658 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
661 opened_file
= str_open(&p_sess
->ftp_arg_str
, kVSFSysStrOpenReadOnly
);
662 if (vsf_sysutil_retval_is_error(opened_file
))
664 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
667 /* Lock file if required */
668 if (tunable_lock_upload_files
)
670 vsf_sysutil_lock_file_read(opened_file
);
672 vsf_sysutil_fstat(opened_file
, &s_p_statbuf
);
673 /* No games please */
674 if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
676 /* Note - pretend open failed */
677 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
678 /* Irritating FireFox does RETR on directories, so avoid logging this
679 * very common and noisy case.
681 if (vsf_sysutil_statbuf_is_dir(s_p_statbuf
))
683 vsf_log_clear_entry(p_sess
);
687 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
690 vsf_sysutil_deactivate_noblock(opened_file
);
691 /* Optionally, we'll be paranoid and only serve publicly readable stuff */
692 if (p_sess
->is_anonymous
&& tunable_anon_world_readable_only
&&
693 !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf
))
695 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
698 /* Set the download offset (from REST) if any */
701 vsf_sysutil_lseek_to(opened_file
, offset
);
703 str_alloc_text(&s_mark_str
, "Opening ");
704 if (tunable_ascii_download_enable
&& p_sess
->is_ascii
)
706 str_append_text(&s_mark_str
, "ASCII");
711 str_append_text(&s_mark_str
, "BINARY");
713 str_append_text(&s_mark_str
, " mode data connection for ");
714 str_append_str(&s_mark_str
, &p_sess
->ftp_arg_str
);
715 str_append_text(&s_mark_str
, " (");
716 str_append_filesize_t(&s_mark_str
,
717 vsf_sysutil_statbuf_get_size(s_p_statbuf
));
718 str_append_text(&s_mark_str
, " bytes).");
721 remote_fd
= VSFTP_COMMAND_FD
;
725 remote_fd
= get_remote_transfer_fd(p_sess
, str_getbuf(&s_mark_str
));
726 if (vsf_sysutil_retval_is_error(remote_fd
))
728 goto port_pasv_cleanup_out
;
731 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
732 opened_file
, 0, is_ascii
);
734 vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 &&
735 trans_ret
.retval
== 0)
737 trans_ret
.retval
= -2;
739 p_sess
->transfer_size
= trans_ret
.transferred
;
740 /* Log _after_ the blocking dispose call, so we get transfer times right */
741 if (trans_ret
.retval
== 0)
743 vsf_log_do_log(p_sess
, 1);
749 /* Emit status message _after_ blocking dispose call to avoid buggy FTP
750 * clients truncating the transfer.
752 if (trans_ret
.retval
== -1)
754 vsf_cmdio_write(p_sess
, FTP_BADSENDFILE
, "Failure reading local file.");
756 else if (trans_ret
.retval
== -2)
758 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
, "Failure writing network stream.");
762 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Transfer complete.");
765 port_pasv_cleanup_out
:
766 port_cleanup(p_sess
);
767 pasv_cleanup(p_sess
);
769 vsf_sysutil_close(opened_file
);
773 handle_list(struct vsf_session
* p_sess
)
775 handle_dir_common(p_sess
, 1, 0);
779 handle_dir_common(struct vsf_session
* p_sess
, int full_details
, int stat_cmd
)
781 static struct mystr s_option_str
;
782 static struct mystr s_filter_str
;
783 static struct mystr s_dir_name_str
;
784 static struct vsf_sysutil_statbuf
* s_p_dirstat
;
785 int dir_allow_read
= 1;
786 struct vsf_sysutil_dir
* p_dir
= 0;
789 str_empty(&s_option_str
);
790 str_empty(&s_filter_str
);
791 /* By default open the current directory */
792 str_alloc_text(&s_dir_name_str
, ".");
793 if (!stat_cmd
&& !data_transfer_checks_ok(p_sess
))
797 /* Do we have an option? Going to be strict here - the option must come
798 * first. e.g. "ls -a .." fine, "ls .. -a" not fine
800 if (!str_isempty(&p_sess
->ftp_arg_str
) &&
801 str_get_char_at(&p_sess
->ftp_arg_str
, 0) == '-')
803 /* Chop off the '-' */
804 str_mid_to_end(&p_sess
->ftp_arg_str
, &s_option_str
, 1);
805 /* A space will separate options from filter (if any) */
806 str_split_char(&s_option_str
, &s_filter_str
, ' ');
810 /* The argument, if any, is just a filter */
811 str_copy(&s_filter_str
, &p_sess
->ftp_arg_str
);
813 if (!str_isempty(&s_filter_str
))
815 resolve_tilde(&s_filter_str
, p_sess
);
816 if (!vsf_access_check_file(&s_filter_str
))
818 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
821 /* First check - is it an outright directory, as in "ls /pub" */
822 p_dir
= str_opendir(&s_filter_str
);
825 /* Listing a directory! */
826 str_copy(&s_dir_name_str
, &s_filter_str
);
827 str_free(&s_filter_str
);
831 struct str_locate_result locate_result
=
832 str_locate_char(&s_filter_str
, '/');
833 if (locate_result
.found
)
835 /* Includes a path! Reverse scan for / in the arg, to get the
836 * base directory and filter (if any)
838 str_copy(&s_dir_name_str
, &s_filter_str
);
839 str_split_char_reverse(&s_dir_name_str
, &s_filter_str
, '/');
840 /* If we have e.g. "ls /.message", we just ripped off the leading
841 * slash because it is the only one!
843 if (str_isempty(&s_dir_name_str
))
845 str_alloc_text(&s_dir_name_str
, "/");
852 /* NOTE - failure check done below, it's not forgotten */
853 p_dir
= str_opendir(&s_dir_name_str
);
859 str_append_char(&s_option_str
, 'a');
860 vsf_cmdio_write_hyphen(p_sess
, FTP_STATFILE_OK
, "Status follows:");
864 int remote_fd
= get_remote_transfer_fd(
865 p_sess
, "Here comes the directory listing.");
866 if (vsf_sysutil_retval_is_error(remote_fd
))
871 if (p_sess
->is_anonymous
&& p_dir
&& tunable_anon_world_readable_only
)
873 vsf_sysutil_dir_stat(p_dir
, &s_p_dirstat
);
874 if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat
))
879 if (p_dir
!= 0 && dir_allow_read
)
881 retval
= vsf_ftpdataio_transfer_dir(p_sess
, use_control
, p_dir
,
882 &s_dir_name_str
, &s_option_str
,
883 &s_filter_str
, full_details
);
887 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && retval
== 0)
894 vsf_cmdio_write(p_sess
, FTP_STATFILE_OK
, "End of status");
896 else if (p_dir
== 0 || !dir_allow_read
)
898 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
,
899 "Transfer done (but failed to open directory).");
901 else if (retval
== 0)
903 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Directory send OK.");
907 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
, "Failure writing network stream.");
913 vsf_sysutil_closedir(p_dir
);
917 port_cleanup(p_sess
);
918 pasv_cleanup(p_sess
);
923 handle_type(struct vsf_session
* p_sess
)
925 str_upper(&p_sess
->ftp_arg_str
);
926 if (str_equal_text(&p_sess
->ftp_arg_str
, "I") ||
927 str_equal_text(&p_sess
->ftp_arg_str
, "L8") ||
928 str_equal_text(&p_sess
->ftp_arg_str
, "L 8"))
930 p_sess
->is_ascii
= 0;
931 vsf_cmdio_write(p_sess
, FTP_TYPEOK
, "Switching to Binary mode.");
933 else if (str_equal_text(&p_sess
->ftp_arg_str
, "A") ||
934 str_equal_text(&p_sess
->ftp_arg_str
, "A N"))
936 p_sess
->is_ascii
= 1;
937 vsf_cmdio_write(p_sess
, FTP_TYPEOK
, "Switching to ASCII mode.");
941 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unrecognised TYPE command.");
946 handle_port(struct vsf_session
* p_sess
)
948 unsigned short the_port
;
949 unsigned char vals
[6];
950 const unsigned char* p_raw
;
951 pasv_cleanup(p_sess
);
952 port_cleanup(p_sess
);
953 p_raw
= vsf_sysutil_parse_uchar_string_sep(&p_sess
->ftp_arg_str
, ',', vals
,
957 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal PORT command.");
960 the_port
= vals
[4] << 8;
962 vsf_sysutil_sockaddr_clone(&p_sess
->p_port_sockaddr
, p_sess
->p_local_addr
);
963 vsf_sysutil_sockaddr_set_ipv4addr(p_sess
->p_port_sockaddr
, vals
);
964 vsf_sysutil_sockaddr_set_port(p_sess
->p_port_sockaddr
, the_port
);
966 * 1) Reject requests not connecting to the control socket IP
967 * 2) Reject connects to privileged ports
969 if (!tunable_port_promiscuous
)
971 if (!vsf_sysutil_sockaddr_addr_equal(p_sess
->p_remote_addr
,
972 p_sess
->p_port_sockaddr
) ||
973 vsf_sysutil_is_port_reserved(the_port
))
975 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal PORT command.");
976 port_cleanup(p_sess
);
980 vsf_cmdio_write(p_sess
, FTP_PORTOK
,
981 "PORT command successful. Consider using PASV.");
985 handle_stor(struct vsf_session
* p_sess
)
987 handle_upload_common(p_sess
, 0, 0);
991 handle_upload_common(struct vsf_session
* p_sess
, int is_append
, int is_unique
)
993 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
994 static struct mystr s_filename
;
995 struct mystr
* p_filename
;
996 struct vsf_transfer_ret trans_ret
;
1001 int do_truncate
= 0;
1002 filesize_t offset
= p_sess
->restart_pos
;
1003 p_sess
->restart_pos
= 0;
1004 if (!data_transfer_checks_ok(p_sess
))
1008 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1009 p_filename
= &p_sess
->ftp_arg_str
;
1012 get_unique_filename(&s_filename
, p_filename
);
1013 p_filename
= &s_filename
;
1015 vsf_log_start_entry(p_sess
, kVSFLogEntryUpload
);
1016 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1017 prepend_path_to_filename(&p_sess
->log_str
);
1018 if (!vsf_access_check_file(p_filename
))
1020 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1023 /* NOTE - actual file permissions will be governed by the tunable umask */
1024 /* XXX - do we care about race between create and chown() of anonymous
1027 if (is_unique
|| (p_sess
->is_anonymous
&& !tunable_anon_other_write_enable
))
1029 new_file_fd
= str_create_exclusive(p_filename
);
1033 /* For non-anonymous, allow open() to overwrite or append existing files */
1034 new_file_fd
= str_create(p_filename
);
1035 if (!is_append
&& offset
== 0)
1040 if (vsf_sysutil_retval_is_error(new_file_fd
))
1042 vsf_cmdio_write(p_sess
, FTP_UPLOADFAIL
, "Could not create file.");
1046 vsf_sysutil_fstat(new_file_fd
, &s_p_statbuf
);
1047 if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1049 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
1050 * such as XFS DMAPI.
1052 vsf_sysutil_deactivate_noblock(new_file_fd
);
1054 /* Are we required to chown() this file for security? */
1055 if (p_sess
->is_anonymous
&& tunable_chown_uploads
)
1057 vsf_sysutil_fchmod(new_file_fd
, tunable_chown_upload_mode
);
1058 if (tunable_one_process_model
)
1060 vsf_one_process_chown_upload(p_sess
, new_file_fd
);
1064 vsf_two_process_chown_upload(p_sess
, new_file_fd
);
1067 /* Are we required to lock this file? */
1068 if (tunable_lock_upload_files
)
1070 vsf_sysutil_lock_file_write(new_file_fd
);
1072 /* Must truncate the file AFTER locking it! */
1075 vsf_sysutil_ftruncate(new_file_fd
);
1076 vsf_sysutil_lseek_to(new_file_fd
, 0);
1078 if (!is_append
&& offset
!= 0)
1080 /* XXX - warning, allows seek past end of file! Check for seek > size? */
1081 vsf_sysutil_lseek_to(new_file_fd
, offset
);
1085 vsf_sysutil_lseek_end(new_file_fd
);
1089 struct mystr resp_str
= INIT_MYSTR
;
1090 str_alloc_text(&resp_str
, "FILE: ");
1091 str_append_str(&resp_str
, p_filename
);
1092 remote_fd
= get_remote_transfer_fd(p_sess
, str_getbuf(&resp_str
));
1093 str_free(&resp_str
);
1097 remote_fd
= get_remote_transfer_fd(p_sess
, "Ok to send data.");
1099 if (vsf_sysutil_retval_is_error(remote_fd
))
1101 goto port_pasv_cleanup_out
;
1103 if (tunable_ascii_upload_enable
&& p_sess
->is_ascii
)
1105 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
1110 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
1113 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && trans_ret
.retval
== 0)
1115 trans_ret
.retval
= -2;
1117 p_sess
->transfer_size
= trans_ret
.transferred
;
1118 if (trans_ret
.retval
== 0)
1121 vsf_log_do_log(p_sess
, 1);
1123 if (trans_ret
.retval
== -1)
1125 vsf_cmdio_write(p_sess
, FTP_BADSENDFILE
, "Failure writing to local file.");
1127 else if (trans_ret
.retval
== -2 || p_sess
->abor_received
)
1129 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
, "Failure reading network stream.");
1133 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Transfer complete.");
1136 port_pasv_cleanup_out
:
1137 port_cleanup(p_sess
);
1138 pasv_cleanup(p_sess
);
1139 if (tunable_delete_failed_uploads
&& created
&& !success
)
1141 str_unlink(p_filename
);
1143 vsf_sysutil_close(new_file_fd
);
1147 handle_mkd(struct vsf_session
* p_sess
)
1150 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1151 vsf_log_start_entry(p_sess
, kVSFLogEntryMkdir
);
1152 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1153 prepend_path_to_filename(&p_sess
->log_str
);
1154 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1156 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1159 /* NOTE! Actual permissions will be governed by the tunable umask */
1160 retval
= str_mkdir(&p_sess
->ftp_arg_str
, 0777);
1163 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1164 "Create directory operation failed.");
1167 vsf_log_do_log(p_sess
, 1);
1169 static struct mystr s_mkd_res
;
1170 static struct mystr s_tmp_str
;
1171 str_copy(&s_tmp_str
, &p_sess
->ftp_arg_str
);
1172 prepend_path_to_filename(&s_tmp_str
);
1173 /* Double up double quotes */
1174 str_replace_text(&s_tmp_str
, "\"", "\"\"");
1175 /* Build result string */
1176 str_alloc_text(&s_mkd_res
, "\"");
1177 str_append_str(&s_mkd_res
, &s_tmp_str
);
1178 str_append_text(&s_mkd_res
, "\" created");
1179 vsf_cmdio_write_str(p_sess
, FTP_MKDIROK
, &s_mkd_res
);
1184 handle_rmd(struct vsf_session
* p_sess
)
1187 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1188 vsf_log_start_entry(p_sess
, kVSFLogEntryRmdir
);
1189 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1190 prepend_path_to_filename(&p_sess
->log_str
);
1191 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1193 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1196 retval
= str_rmdir(&p_sess
->ftp_arg_str
);
1199 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1200 "Remove directory operation failed.");
1204 vsf_log_do_log(p_sess
, 1);
1205 vsf_cmdio_write(p_sess
, FTP_RMDIROK
,
1206 "Remove directory operation successful.");
1211 handle_dele(struct vsf_session
* p_sess
)
1214 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1215 vsf_log_start_entry(p_sess
, kVSFLogEntryDelete
);
1216 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1217 prepend_path_to_filename(&p_sess
->log_str
);
1218 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1220 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1223 retval
= str_unlink(&p_sess
->ftp_arg_str
);
1226 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Delete operation failed.");
1230 vsf_log_do_log(p_sess
, 1);
1231 vsf_cmdio_write(p_sess
, FTP_DELEOK
, "Delete operation successful.");
1236 handle_rest(struct vsf_session
* p_sess
)
1238 static struct mystr s_rest_str
;
1239 filesize_t val
= str_a_to_filesize_t(&p_sess
->ftp_arg_str
);
1244 p_sess
->restart_pos
= val
;
1245 str_alloc_text(&s_rest_str
, "Restart position accepted (");
1246 str_append_filesize_t(&s_rest_str
, val
);
1247 str_append_text(&s_rest_str
, ").");
1248 vsf_cmdio_write_str(p_sess
, FTP_RESTOK
, &s_rest_str
);
1252 handle_rnfr(struct vsf_session
* p_sess
)
1254 static struct vsf_sysutil_statbuf
* p_statbuf
;
1256 /* Clear old value */
1257 str_free(&p_sess
->rnfr_filename_str
);
1258 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1259 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1261 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1262 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1263 prepend_path_to_filename(&p_sess
->log_str
);
1264 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1267 /* Does it exist? */
1268 retval
= str_stat(&p_sess
->ftp_arg_str
, &p_statbuf
);
1272 str_copy(&p_sess
->rnfr_filename_str
, &p_sess
->ftp_arg_str
);
1273 vsf_cmdio_write(p_sess
, FTP_RNFROK
, "Ready for RNTO.");
1277 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1278 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1279 prepend_path_to_filename(&p_sess
->log_str
);
1280 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "RNFR command failed.");
1285 handle_rnto(struct vsf_session
* p_sess
)
1287 static struct mystr s_tmp_str
;
1289 /* If we didn't get a RNFR, throw a wobbly */
1290 if (str_isempty(&p_sess
->rnfr_filename_str
))
1292 vsf_cmdio_write(p_sess
, FTP_NEEDRNFR
,
1293 "RNFR required first.");
1296 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1297 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1298 str_copy(&p_sess
->log_str
, &p_sess
->rnfr_filename_str
);
1299 prepend_path_to_filename(&p_sess
->log_str
);
1300 str_append_char(&p_sess
->log_str
, ' ');
1301 str_copy(&s_tmp_str
, &p_sess
->ftp_arg_str
);
1302 prepend_path_to_filename(&s_tmp_str
);
1303 str_append_str(&p_sess
->log_str
, &s_tmp_str
);
1304 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1306 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1309 /* NOTE - might overwrite destination file. Not a concern because the same
1310 * could be accomplished with DELE.
1312 retval
= str_rename(&p_sess
->rnfr_filename_str
, &p_sess
->ftp_arg_str
);
1313 /* Clear the RNFR filename; start the two stage process again! */
1314 str_free(&p_sess
->rnfr_filename_str
);
1317 vsf_log_do_log(p_sess
, 1);
1318 vsf_cmdio_write(p_sess
, FTP_RENAMEOK
, "Rename successful.");
1322 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Rename failed.");
1327 handle_nlst(struct vsf_session
* p_sess
)
1329 handle_dir_common(p_sess
, 0, 0);
1333 prepend_path_to_filename(struct mystr
* p_str
)
1335 static struct mystr s_tmp_str
;
1336 /* Only prepend current working directory if the incoming filename is
1339 str_empty(&s_tmp_str
);
1340 if (str_isempty(p_str
) || str_get_char_at(p_str
, 0) != '/')
1342 str_getcwd(&s_tmp_str
);
1343 /* Careful to not emit // if we are in directory / (common with chroot) */
1344 if (str_isempty(&s_tmp_str
) ||
1345 str_get_char_at(&s_tmp_str
, str_getlen(&s_tmp_str
) - 1) != '/')
1347 str_append_char(&s_tmp_str
, '/');
1350 str_append_str(&s_tmp_str
, p_str
);
1351 str_copy(p_str
, &s_tmp_str
);
1356 handle_sigurg(void* p_private
)
1358 struct mystr async_cmd_str
= INIT_MYSTR
;
1359 struct mystr async_arg_str
= INIT_MYSTR
;
1360 struct mystr real_cmd_str
= INIT_MYSTR
;
1362 struct vsf_session
* p_sess
= (struct vsf_session
*) p_private
;
1363 /* Did stupid client sent something OOB without a data connection? */
1364 if (p_sess
->data_fd
== -1)
1368 /* Get the async command - blocks (use data timeout alarm) */
1369 vsf_cmdio_get_cmd_and_arg(p_sess
, &async_cmd_str
, &async_arg_str
, 0);
1370 /* Chop off first four characters; they are telnet characters. The client
1371 * should have sent the first two normally and the second two as urgent
1374 len
= str_getlen(&async_cmd_str
);
1377 str_right(&async_cmd_str
, &real_cmd_str
, len
- 4);
1379 if (str_equal_text(&real_cmd_str
, "ABOR"))
1381 p_sess
->abor_received
= 1;
1382 /* This is failok because of a small race condition; the SIGURG might
1383 * be raised after the data socket is closed, but before data_fd is
1386 vsf_sysutil_shutdown_failok(p_sess
->data_fd
);
1391 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown command.");
1393 str_free(&async_cmd_str
);
1394 str_free(&async_arg_str
);
1395 str_free(&real_cmd_str
);
1399 get_remote_transfer_fd(struct vsf_session
* p_sess
, const char* p_status_msg
)
1402 if (!pasv_active(p_sess
) && !port_active(p_sess
))
1404 bug("neither PORT nor PASV active in get_remote_transfer_fd");
1406 p_sess
->abor_received
= 0;
1407 if (pasv_active(p_sess
))
1409 remote_fd
= vsf_ftpdataio_get_pasv_fd(p_sess
);
1413 remote_fd
= vsf_ftpdataio_get_port_fd(p_sess
);
1415 if (vsf_sysutil_retval_is_error(remote_fd
))
1419 vsf_cmdio_write(p_sess
, FTP_DATACONN
, p_status_msg
);
1420 if (vsf_ftpdataio_post_mark_connect(p_sess
) != 1)
1422 vsf_ftpdataio_dispose_transfer_fd(p_sess
);
1429 check_abor(struct vsf_session
* p_sess
)
1431 /* If the client sent ABOR, respond to it here */
1432 if (p_sess
->abor_received
)
1434 p_sess
->abor_received
= 0;
1435 vsf_cmdio_write(p_sess
, FTP_ABOROK
, "ABOR successful.");
1440 handle_size(struct vsf_session
* p_sess
)
1442 /* Note - in ASCII mode, are supposed to return the size after taking into
1443 * account ASCII linefeed conversions. At least this is what wu-ftpd does in
1444 * version 2.6.1. Proftpd-1.2.0pre fails to do this.
1445 * I will not do it because it is a potential I/O DoS.
1447 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1449 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1450 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1452 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1455 retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1456 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1458 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Could not get file size.");
1462 static struct mystr s_size_res_str
;
1463 str_alloc_filesize_t(&s_size_res_str
,
1464 vsf_sysutil_statbuf_get_size(s_p_statbuf
));
1465 vsf_cmdio_write_str(p_sess
, FTP_SIZEOK
, &s_size_res_str
);
1470 handle_site(struct vsf_session
* p_sess
)
1472 static struct mystr s_site_args_str
;
1473 /* What SITE sub-command is it? */
1474 str_split_char(&p_sess
->ftp_arg_str
, &s_site_args_str
, ' ');
1475 str_upper(&p_sess
->ftp_arg_str
);
1476 if (tunable_write_enable
&&
1477 tunable_chmod_enable
&&
1478 str_equal_text(&p_sess
->ftp_arg_str
, "CHMOD"))
1480 handle_site_chmod(p_sess
, &s_site_args_str
);
1482 else if (str_equal_text(&p_sess
->ftp_arg_str
, "UMASK"))
1484 handle_site_umask(p_sess
, &s_site_args_str
);
1486 else if (str_equal_text(&p_sess
->ftp_arg_str
, "HELP"))
1488 vsf_cmdio_write(p_sess
, FTP_SITEHELP
, "CHMOD UMASK HELP");
1492 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown SITE command.");
1497 handle_site_chmod(struct vsf_session
* p_sess
, struct mystr
* p_arg_str
)
1499 static struct mystr s_chmod_file_str
;
1502 if (str_isempty(p_arg_str
))
1504 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "SITE CHMOD needs 2 arguments.");
1507 str_split_char(p_arg_str
, &s_chmod_file_str
, ' ');
1508 if (str_isempty(&s_chmod_file_str
))
1510 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "SITE CHMOD needs 2 arguments.");
1513 resolve_tilde(&s_chmod_file_str
, p_sess
);
1514 vsf_log_start_entry(p_sess
, kVSFLogEntryChmod
);
1515 str_copy(&p_sess
->log_str
, &s_chmod_file_str
);
1516 prepend_path_to_filename(&p_sess
->log_str
);
1517 str_append_char(&p_sess
->log_str
, ' ');
1518 str_append_str(&p_sess
->log_str
, p_arg_str
);
1519 if (!vsf_access_check_file(&s_chmod_file_str
))
1521 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1524 /* Don't worry - our chmod() implementation only allows 0 - 0777 */
1525 perms
= str_octal_to_uint(p_arg_str
);
1526 retval
= str_chmod(&s_chmod_file_str
, perms
);
1527 if (vsf_sysutil_retval_is_error(retval
))
1529 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "SITE CHMOD command failed.");
1533 vsf_log_do_log(p_sess
, 1);
1534 vsf_cmdio_write(p_sess
, FTP_CHMODOK
, "SITE CHMOD command ok.");
1539 handle_site_umask(struct vsf_session
* p_sess
, struct mystr
* p_arg_str
)
1541 static struct mystr s_umask_resp_str
;
1542 if (str_isempty(p_arg_str
))
1544 /* Empty arg => report current umask */
1545 str_alloc_text(&s_umask_resp_str
, "Your current UMASK is ");
1546 str_append_text(&s_umask_resp_str
,
1547 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1551 /* Set current umask */
1552 unsigned int new_umask
= str_octal_to_uint(p_arg_str
);
1553 vsf_sysutil_set_umask(new_umask
);
1554 str_alloc_text(&s_umask_resp_str
, "UMASK set to ");
1555 str_append_text(&s_umask_resp_str
,
1556 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1558 vsf_cmdio_write_str(p_sess
, FTP_UMASKOK
, &s_umask_resp_str
);
1562 handle_appe(struct vsf_session
* p_sess
)
1564 handle_upload_common(p_sess
, 1, 0);
1568 handle_mdtm(struct vsf_session
* p_sess
)
1570 static struct mystr s_filename_str
;
1571 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1574 struct str_locate_result loc
= str_locate_char(&p_sess
->ftp_arg_str
, ' ');
1575 int retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1576 if (tunable_mdtm_write
&& retval
!= 0 && loc
.found
&&
1577 vsf_sysutil_isdigit(str_get_char_at(&p_sess
->ftp_arg_str
, 0)))
1579 if (loc
.index
== 8 || loc
.index
== 14 ||
1580 (loc
.index
> 15 && str_get_char_at(&p_sess
->ftp_arg_str
, 14) == '.'))
1587 str_split_char(&p_sess
->ftp_arg_str
, &s_filename_str
, ' ');
1588 modtime
= vsf_sysutil_parse_time(str_getbuf(&p_sess
->ftp_arg_str
));
1589 str_copy(&p_sess
->ftp_arg_str
, &s_filename_str
);
1591 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1592 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1594 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1597 if (do_write
&& tunable_write_enable
&&
1598 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
))
1600 retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1601 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1603 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1604 "Could not set file modification time.");
1608 retval
= vsf_sysutil_setmodtime(
1609 str_getbuf(&p_sess
->ftp_arg_str
), modtime
, tunable_use_localtime
);
1612 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1613 "Could not set file modification time.");
1617 vsf_cmdio_write(p_sess
, FTP_MDTMOK
,
1618 "File modification time set.");
1624 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1626 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1627 "Could not get file modification time.");
1631 static struct mystr s_mdtm_res_str
;
1632 str_alloc_text(&s_mdtm_res_str
,
1633 vsf_sysutil_statbuf_get_numeric_date(
1634 s_p_statbuf
, tunable_use_localtime
));
1635 vsf_cmdio_write_str(p_sess
, FTP_MDTMOK
, &s_mdtm_res_str
);
1641 handle_eprt(struct vsf_session
* p_sess
)
1643 static struct mystr s_part1_str
;
1644 static struct mystr s_part2_str
;
1645 static struct mystr s_scopeid_str
;
1648 const unsigned char* p_raw_addr
;
1649 int is_ipv6
= vsf_sysutil_sockaddr_is_ipv6(p_sess
->p_local_addr
);
1650 port_cleanup(p_sess
);
1651 pasv_cleanup(p_sess
);
1652 str_copy(&s_part1_str
, &p_sess
->ftp_arg_str
);
1653 str_split_char(&s_part1_str
, &s_part2_str
, '|');
1654 if (!str_isempty(&s_part1_str
))
1658 /* Split out the protocol and check it */
1659 str_split_char(&s_part2_str
, &s_part1_str
, '|');
1660 proto
= str_atoi(&s_part2_str
);
1661 if (proto
< 1 || proto
> 2 || (!is_ipv6
&& proto
== 2))
1663 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Bad EPRT protocol.");
1666 /* Split out address and parse it */
1667 str_split_char(&s_part1_str
, &s_part2_str
, '|');
1670 str_split_char(&s_part1_str
, &s_scopeid_str
, '%');
1671 p_raw_addr
= vsf_sysutil_parse_ipv6(&s_part1_str
);
1675 p_raw_addr
= vsf_sysutil_parse_ipv4(&s_part1_str
);
1681 /* Split out port and parse it */
1682 str_split_char(&s_part2_str
, &s_part1_str
, '|');
1683 if (!str_isempty(&s_part1_str
) || str_isempty(&s_part2_str
))
1687 port
= str_atoi(&s_part2_str
);
1688 if (port
< 0 || port
> 65535)
1692 vsf_sysutil_sockaddr_clone(&p_sess
->p_port_sockaddr
, p_sess
->p_local_addr
);
1695 vsf_sysutil_sockaddr_set_ipv6addr(p_sess
->p_port_sockaddr
, p_raw_addr
);
1699 vsf_sysutil_sockaddr_set_ipv4addr(p_sess
->p_port_sockaddr
, p_raw_addr
);
1701 vsf_sysutil_sockaddr_set_port(p_sess
->p_port_sockaddr
, (unsigned short) port
);
1703 * 1) Reject requests not connecting to the control socket IP
1704 * 2) Reject connects to privileged ports
1706 if (!tunable_port_promiscuous
)
1708 if (!vsf_sysutil_sockaddr_addr_equal(p_sess
->p_remote_addr
,
1709 p_sess
->p_port_sockaddr
) ||
1710 vsf_sysutil_is_port_reserved(port
))
1712 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal EPRT command.");
1713 port_cleanup(p_sess
);
1717 vsf_cmdio_write(p_sess
, FTP_EPRTOK
,
1718 "EPRT command successful. Consider using EPSV.");
1721 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Bad EPRT command.");
1724 /* XXX - add AUTH etc. */
1726 handle_help(struct vsf_session
* p_sess
)
1728 vsf_cmdio_write_hyphen(p_sess
, FTP_HELP
,
1729 "The following commands are recognized.");
1730 vsf_cmdio_write_raw(p_sess
,
1731 " ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n");
1732 vsf_cmdio_write_raw(p_sess
,
1733 " MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n");
1734 vsf_cmdio_write_raw(p_sess
,
1735 " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
1736 vsf_cmdio_write_raw(p_sess
,
1738 vsf_cmdio_write(p_sess
, FTP_HELP
, "Help OK.");
1742 handle_stou(struct vsf_session
* p_sess
)
1744 handle_upload_common(p_sess
, 0, 1);
1748 get_unique_filename(struct mystr
* p_outstr
, const struct mystr
* p_base_str
)
1750 /* Use silly wu-ftpd algorithm for compatibility. It has races of course, if
1751 * two sessions are using the same file prefix at the same time.
1753 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1754 unsigned int suffix
= 1;
1755 /* Do not add any suffix at all if the name is not taken. */
1756 int retval
= str_stat(p_base_str
, &s_p_statbuf
);
1757 if (vsf_sysutil_retval_is_error(retval
))
1759 str_copy(p_outstr
, p_base_str
);
1764 str_copy(p_outstr
, p_base_str
);
1765 str_append_char(p_outstr
, '.');
1766 str_append_ulong(p_outstr
, suffix
);
1767 retval
= str_stat(p_outstr
, &s_p_statbuf
);
1768 if (vsf_sysutil_retval_is_error(retval
))
1777 handle_stat(struct vsf_session
* p_sess
)
1779 vsf_cmdio_write_hyphen(p_sess
, FTP_STATOK
, "FTP server status:");
1780 vsf_cmdio_write_raw(p_sess
, " Connected to ");
1781 vsf_cmdio_write_raw(p_sess
, str_getbuf(&p_sess
->remote_ip_str
));
1782 vsf_cmdio_write_raw(p_sess
, "\r\n");
1783 vsf_cmdio_write_raw(p_sess
, " Logged in as ");
1784 vsf_cmdio_write_raw(p_sess
, str_getbuf(&p_sess
->user_str
));
1785 vsf_cmdio_write_raw(p_sess
, "\r\n");
1786 vsf_cmdio_write_raw(p_sess
, " TYPE: ");
1787 if (p_sess
->is_ascii
)
1789 vsf_cmdio_write_raw(p_sess
, "ASCII\r\n");
1793 vsf_cmdio_write_raw(p_sess
, "BINARY\r\n");
1795 if (p_sess
->bw_rate_max
== 0)
1797 vsf_cmdio_write_raw(p_sess
, " No session bandwidth limit\r\n");
1801 vsf_cmdio_write_raw(p_sess
, " Session bandwidth limit in byte/s is ");
1802 vsf_cmdio_write_raw(p_sess
, vsf_sysutil_ulong_to_str(p_sess
->bw_rate_max
));
1803 vsf_cmdio_write_raw(p_sess
, "\r\n");
1805 if (tunable_idle_session_timeout
== 0)
1807 vsf_cmdio_write_raw(p_sess
, " No session timeout\r\n");
1811 vsf_cmdio_write_raw(p_sess
, " Session timeout in seconds is ");
1812 vsf_cmdio_write_raw(p_sess
,
1813 vsf_sysutil_ulong_to_str(tunable_idle_session_timeout
));
1814 vsf_cmdio_write_raw(p_sess
, "\r\n");
1816 if (p_sess
->control_use_ssl
)
1818 vsf_cmdio_write_raw(p_sess
, " Control connection is encrypted\r\n");
1822 vsf_cmdio_write_raw(p_sess
, " Control connection is plain text\r\n");
1824 if (p_sess
->data_use_ssl
)
1826 vsf_cmdio_write_raw(p_sess
, " Data connections will be encrypted\r\n");
1830 vsf_cmdio_write_raw(p_sess
, " Data connections will be plain text\r\n");
1832 if (p_sess
->num_clients
> 0)
1834 vsf_cmdio_write_raw(p_sess
, " At session startup, client count was ");
1835 vsf_cmdio_write_raw(p_sess
, vsf_sysutil_ulong_to_str(p_sess
->num_clients
));
1836 vsf_cmdio_write_raw(p_sess
, "\r\n");
1838 vsf_cmdio_write_raw(p_sess
,
1839 " vsFTPd " VSF_VERSION
" - secure, fast, stable\r\n");
1840 vsf_cmdio_write(p_sess
, FTP_STATOK
, "End of status");
1844 handle_stat_file(struct vsf_session
* p_sess
)
1846 handle_dir_common(p_sess
, 1, 1);
1850 data_transfer_checks_ok(struct vsf_session
* p_sess
)
1852 if (!pasv_active(p_sess
) && !port_active(p_sess
))
1854 vsf_cmdio_write(p_sess
, FTP_BADSENDCONN
, "Use PORT or PASV first.");
1857 if (tunable_ssl_enable
&& !p_sess
->data_use_ssl
&&
1858 ((tunable_force_local_data_ssl
&& !p_sess
->is_anonymous
) ||
1859 (tunable_force_anon_data_ssl
&& p_sess
->is_anonymous
)))
1862 p_sess
, FTP_NEEDENCRYPT
, "Data connections must be encrypted.");
1869 resolve_tilde(struct mystr
* p_str
, struct vsf_session
* p_sess
)
1871 unsigned int len
= str_getlen(p_str
);
1872 if (len
> 0 && str_get_char_at(p_str
, 0) == '~')
1874 static struct mystr s_rhs_str
;
1875 if (len
== 1 || str_get_char_at(p_str
, 1) == '/')
1877 str_split_char(p_str
, &s_rhs_str
, '~');
1878 str_copy(p_str
, &p_sess
->home_str
);
1879 str_append_str(p_str
, &s_rhs_str
);
1881 else if (tunable_tilde_user_enable
&& len
> 1)
1883 static struct mystr s_user_str
;
1884 struct vsf_sysutil_user
* p_user
;
1885 str_copy(&s_rhs_str
, p_str
);
1886 str_split_char(&s_rhs_str
, &s_user_str
, '~');
1887 str_split_char(&s_user_str
, &s_rhs_str
, '/');
1888 p_user
= str_getpwnam(&s_user_str
);
1891 str_alloc_text(p_str
, vsf_sysutil_user_get_homedir(p_user
));
1892 if (!str_isempty(&s_rhs_str
))
1894 str_append_char(p_str
, '/');
1895 str_append_str(p_str
, &s_rhs_str
);
1902 static void handle_logged_in_user(struct vsf_session
* p_sess
)
1904 if (p_sess
->is_anonymous
)
1906 vsf_cmdio_write(p_sess
, FTP_LOGINERR
, "Can't change from guest user.");
1908 else if (str_equal(&p_sess
->user_str
, &p_sess
->ftp_arg_str
))
1910 vsf_cmdio_write(p_sess
, FTP_GIVEPWORD
, "Any password will do.");
1914 vsf_cmdio_write(p_sess
, FTP_LOGINERR
, "Can't change to another user.");
1918 static void handle_logged_in_pass(struct vsf_session
* p_sess
)
1920 vsf_cmdio_write(p_sess
, FTP_LOGINOK
, "Already logged in.");
1924 handle_http(struct vsf_session
* p_sess
)
1926 /* Warning: Doesn't respect cmds_allowed etc. because there is currently only
1927 * one command (GET)!
1928 * HTTP likely doesn't respect other important FTP options. I don't think
1931 if (!tunable_download_enable
)
1933 bug("HTTP needs download - fix your config");
1935 /* Eat the HTTP headers, which we don't care about. */
1938 vsf_cmdio_get_cmd_and_arg(p_sess
, &p_sess
->ftp_cmd_str
,
1939 &p_sess
->ftp_arg_str
, 1);
1941 while (!str_isempty(&p_sess
->ftp_cmd_str
) ||
1942 !str_isempty(&p_sess
->ftp_arg_str
));
1943 vsf_cmdio_write_raw(p_sess
, "HTTP/1.1 200 OK\r\n");
1944 vsf_cmdio_write_raw(p_sess
, "Server: vsftpd\r\n");
1945 vsf_cmdio_write_raw(p_sess
, "Connection: close\r\n");
1946 vsf_cmdio_write_raw(p_sess
, "X-Frame-Options: SAMEORIGIN\r\n");
1947 vsf_cmdio_write_raw(p_sess
, "X-Content-Type-Options: nosniff\r\n");
1948 /* Split the path from the HTTP/1.x */
1949 str_split_char(&p_sess
->http_get_arg
, &p_sess
->ftp_arg_str
, ' ');
1950 str_copy(&p_sess
->ftp_arg_str
, &p_sess
->http_get_arg
);
1951 str_split_char(&p_sess
->http_get_arg
, &p_sess
->ftp_cmd_str
, '.');
1952 str_upper(&p_sess
->ftp_cmd_str
);
1953 if (str_equal_text(&p_sess
->ftp_cmd_str
, "HTML") ||
1954 str_equal_text(&p_sess
->ftp_cmd_str
, "HTM"))
1956 vsf_cmdio_write_raw(p_sess
, "Content-Type: text/html\r\n");
1960 vsf_cmdio_write_raw(p_sess
, "Content-Type: dunno\r\n");
1962 vsf_cmdio_write_raw(p_sess
, "\r\n");
1963 p_sess
->is_ascii
= 0;
1964 p_sess
->restart_pos
= 0;
1965 handle_retr(p_sess
, 1);
1966 if (vsf_log_entry_pending(p_sess
))
1968 vsf_log_do_log(p_sess
, 0);
1970 vsf_sysutil_exit(0);