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
);
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
);
64 static int pasv_active(struct vsf_session
* p_sess
);
65 static int port_active(struct vsf_session
* p_sess
);
66 static void pasv_cleanup(struct vsf_session
* p_sess
);
67 static void port_cleanup(struct vsf_session
* p_sess
);
68 static void handle_dir_common(struct vsf_session
* p_sess
, int full_details
,
70 static void prepend_path_to_filename(struct mystr
* p_str
);
71 static int get_remote_transfer_fd(struct vsf_session
* p_sess
,
72 const char* p_status_msg
);
73 static void check_abor(struct vsf_session
* p_sess
);
74 static void handle_sigurg(void* p_private
);
75 static void handle_upload_common(struct vsf_session
* p_sess
, int is_append
,
77 static void get_unique_filename(struct mystr
* p_outstr
,
78 const struct mystr
* p_base
);
79 static int data_transfer_checks_ok(struct vsf_session
* p_sess
);
80 static void resolve_tilde(struct mystr
* p_str
, struct vsf_session
* p_sess
);
83 process_post_login(struct vsf_session
* p_sess
)
85 str_getcwd(&p_sess
->home_str
);
86 if (p_sess
->is_anonymous
)
88 vsf_sysutil_set_umask(tunable_anon_umask
);
89 p_sess
->bw_rate_max
= tunable_anon_max_rate
;
93 vsf_sysutil_set_umask(tunable_local_umask
);
94 p_sess
->bw_rate_max
= tunable_local_max_rate
;
96 if (tunable_async_abor_enable
)
98 vsf_sysutil_install_sighandler(kVSFSysUtilSigURG
, handle_sigurg
, p_sess
, 0);
99 vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD
);
101 /* Handle any login message */
102 vsf_banner_dir_changed(p_sess
, FTP_LOGINOK
);
103 vsf_cmdio_write(p_sess
, FTP_LOGINOK
, "Login successful.");
107 if (tunable_setproctitle_enable
)
109 vsf_sysutil_setproctitle("IDLE");
112 vsf_cmdio_get_cmd_and_arg(p_sess
, &p_sess
->ftp_cmd_str
,
113 &p_sess
->ftp_arg_str
, 1);
114 if (tunable_setproctitle_enable
)
116 struct mystr proctitle_str
= INIT_MYSTR
;
117 str_copy(&proctitle_str
, &p_sess
->ftp_cmd_str
);
118 if (!str_isempty(&p_sess
->ftp_arg_str
))
120 str_append_char(&proctitle_str
, ' ');
121 str_append_str(&proctitle_str
, &p_sess
->ftp_arg_str
);
123 /* Suggestion from Solar */
124 str_replace_unprintable(&proctitle_str
, '?');
125 vsf_sysutil_setproctitle_str(&proctitle_str
);
126 str_free(&proctitle_str
);
128 /* Test command against the allowed lists.. */
129 if (tunable_cmds_allowed
)
131 static struct mystr s_src_str
;
132 static struct mystr s_rhs_str
;
133 str_alloc_text(&s_src_str
, tunable_cmds_allowed
);
136 str_split_char(&s_src_str
, &s_rhs_str
, ',');
137 if (str_isempty(&s_src_str
))
142 else if (str_equal(&s_src_str
, &p_sess
->ftp_cmd_str
))
146 str_copy(&s_src_str
, &s_rhs_str
);
149 if (tunable_cmds_denied
)
151 static struct mystr s_src_str
;
152 static struct mystr s_rhs_str
;
153 str_alloc_text(&s_src_str
, tunable_cmds_denied
);
156 str_split_char(&s_src_str
, &s_rhs_str
, ',');
157 if (str_isempty(&s_src_str
))
161 else if (str_equal(&s_src_str
, &p_sess
->ftp_cmd_str
))
166 str_copy(&s_src_str
, &s_rhs_str
);
171 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
173 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "QUIT"))
175 vsf_cmdio_write_exit(p_sess
, FTP_GOODBYE
, "Goodbye.");
177 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PWD") ||
178 str_equal_text(&p_sess
->ftp_cmd_str
, "XPWD"))
182 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "CWD") ||
183 str_equal_text(&p_sess
->ftp_cmd_str
, "XCWD"))
187 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "CDUP") ||
188 str_equal_text(&p_sess
->ftp_cmd_str
, "XCUP"))
192 else if (tunable_pasv_enable
&&
194 (str_equal_text(&p_sess
->ftp_cmd_str
, "PASV") ||
195 str_equal_text(&p_sess
->ftp_cmd_str
, "P@SW")))
197 handle_pasv(p_sess
, 0);
199 else if (tunable_pasv_enable
&&
200 str_equal_text(&p_sess
->ftp_cmd_str
, "EPSV"))
202 handle_pasv(p_sess
, 1);
204 else if (tunable_download_enable
&&
205 str_equal_text(&p_sess
->ftp_cmd_str
, "RETR"))
209 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "NOOP"))
211 vsf_cmdio_write(p_sess
, FTP_NOOPOK
, "NOOP ok.");
213 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SYST"))
215 vsf_cmdio_write(p_sess
, FTP_SYSTOK
, "UNIX Type: L8");
217 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "HELP"))
221 else if (tunable_dirlist_enable
&&
222 str_equal_text(&p_sess
->ftp_cmd_str
, "LIST"))
226 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "TYPE"))
230 else if (tunable_port_enable
&&
232 str_equal_text(&p_sess
->ftp_cmd_str
, "PORT"))
236 else if (tunable_write_enable
&&
237 (tunable_anon_upload_enable
|| !p_sess
->is_anonymous
) &&
238 str_equal_text(&p_sess
->ftp_cmd_str
, "STOR"))
242 else if (tunable_write_enable
&&
243 (tunable_anon_mkdir_write_enable
|| !p_sess
->is_anonymous
) &&
244 (str_equal_text(&p_sess
->ftp_cmd_str
, "MKD") ||
245 str_equal_text(&p_sess
->ftp_cmd_str
, "XMKD")))
249 else if (tunable_write_enable
&&
250 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
251 (str_equal_text(&p_sess
->ftp_cmd_str
, "RMD") ||
252 str_equal_text(&p_sess
->ftp_cmd_str
, "XRMD")))
256 else if (tunable_write_enable
&&
257 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
258 str_equal_text(&p_sess
->ftp_cmd_str
, "DELE"))
262 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "REST"))
266 else if (tunable_write_enable
&&
267 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
268 str_equal_text(&p_sess
->ftp_cmd_str
, "RNFR"))
272 else if (tunable_write_enable
&&
273 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
274 str_equal_text(&p_sess
->ftp_cmd_str
, "RNTO"))
278 else if (tunable_dirlist_enable
&&
279 str_equal_text(&p_sess
->ftp_cmd_str
, "NLST"))
283 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SIZE"))
287 else if (!p_sess
->is_anonymous
&&
288 str_equal_text(&p_sess
->ftp_cmd_str
, "SITE"))
292 /* Note - the weird ABOR string is checking for an async ABOR arriving
293 * without a SIGURG condition.
295 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ABOR") ||
296 str_equal_text(&p_sess
->ftp_cmd_str
, "\377\364\377\362ABOR"))
298 vsf_cmdio_write(p_sess
, FTP_ABOR_NOCONN
, "No transfer to ABOR.");
300 else if (tunable_write_enable
&&
301 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
) &&
302 str_equal_text(&p_sess
->ftp_cmd_str
, "APPE"))
306 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "MDTM"))
310 else if (tunable_port_enable
&&
311 str_equal_text(&p_sess
->ftp_cmd_str
, "EPRT"))
315 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "STRU"))
317 str_upper(&p_sess
->ftp_arg_str
);
318 if (str_equal_text(&p_sess
->ftp_arg_str
, "F"))
320 vsf_cmdio_write(p_sess
, FTP_STRUOK
, "Structure set to F.");
324 vsf_cmdio_write(p_sess
, FTP_BADSTRU
, "Bad STRU command.");
327 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "MODE"))
329 str_upper(&p_sess
->ftp_arg_str
);
330 if (str_equal_text(&p_sess
->ftp_arg_str
, "S"))
332 vsf_cmdio_write(p_sess
, FTP_MODEOK
, "Mode set to S.");
336 vsf_cmdio_write(p_sess
, FTP_BADMODE
, "Bad MODE command.");
339 else if (tunable_write_enable
&&
340 (tunable_anon_upload_enable
|| !p_sess
->is_anonymous
) &&
341 str_equal_text(&p_sess
->ftp_cmd_str
, "STOU"))
345 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ALLO"))
347 vsf_cmdio_write(p_sess
, FTP_ALLOOK
, "ALLO command ignored.");
349 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "REIN"))
351 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "REIN not implemented.");
353 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "ACCT"))
355 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "ACCT not implemented.");
357 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "SMNT"))
359 vsf_cmdio_write(p_sess
, FTP_COMMANDNOTIMPL
, "SMNT not implemented.");
361 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "FEAT"))
365 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "OPTS"))
369 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "STAT") &&
370 str_isempty(&p_sess
->ftp_arg_str
))
374 else if (tunable_dirlist_enable
&&
375 str_equal_text(&p_sess
->ftp_cmd_str
, "STAT"))
377 handle_stat_file(p_sess
);
379 else if (tunable_ssl_enable
&& str_equal_text(&p_sess
->ftp_cmd_str
, "PBSZ"))
383 else if (tunable_ssl_enable
&& str_equal_text(&p_sess
->ftp_cmd_str
, "PROT"))
387 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "USER"))
389 handle_logged_in_user(p_sess
);
391 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PASS"))
393 handle_logged_in_pass(p_sess
);
395 else if (str_equal_text(&p_sess
->ftp_cmd_str
, "PASV") ||
396 str_equal_text(&p_sess
->ftp_cmd_str
, "PORT") ||
397 str_equal_text(&p_sess
->ftp_cmd_str
, "STOR") ||
398 str_equal_text(&p_sess
->ftp_cmd_str
, "MKD") ||
399 str_equal_text(&p_sess
->ftp_cmd_str
, "XMKD") ||
400 str_equal_text(&p_sess
->ftp_cmd_str
, "RMD") ||
401 str_equal_text(&p_sess
->ftp_cmd_str
, "XRMD") ||
402 str_equal_text(&p_sess
->ftp_cmd_str
, "DELE") ||
403 str_equal_text(&p_sess
->ftp_cmd_str
, "RNFR") ||
404 str_equal_text(&p_sess
->ftp_cmd_str
, "RNTO") ||
405 str_equal_text(&p_sess
->ftp_cmd_str
, "SITE") ||
406 str_equal_text(&p_sess
->ftp_cmd_str
, "APPE") ||
407 str_equal_text(&p_sess
->ftp_cmd_str
, "EPSV") ||
408 str_equal_text(&p_sess
->ftp_cmd_str
, "EPRT") ||
409 str_equal_text(&p_sess
->ftp_cmd_str
, "RETR") ||
410 str_equal_text(&p_sess
->ftp_cmd_str
, "LIST") ||
411 str_equal_text(&p_sess
->ftp_cmd_str
, "NLST") ||
412 str_equal_text(&p_sess
->ftp_cmd_str
, "STOU") ||
413 str_equal_text(&p_sess
->ftp_cmd_str
, "ALLO") ||
414 str_equal_text(&p_sess
->ftp_cmd_str
, "REIN") ||
415 str_equal_text(&p_sess
->ftp_cmd_str
, "ACCT") ||
416 str_equal_text(&p_sess
->ftp_cmd_str
, "SMNT") ||
417 str_equal_text(&p_sess
->ftp_cmd_str
, "FEAT") ||
418 str_equal_text(&p_sess
->ftp_cmd_str
, "OPTS") ||
419 str_equal_text(&p_sess
->ftp_cmd_str
, "STAT") ||
420 str_equal_text(&p_sess
->ftp_cmd_str
, "PBSZ") ||
421 str_equal_text(&p_sess
->ftp_cmd_str
, "PROT"))
423 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
425 else if (str_isempty(&p_sess
->ftp_cmd_str
) &&
426 str_isempty(&p_sess
->ftp_arg_str
))
428 /* Deliberately ignore to avoid NAT device bugs. ProFTPd does the same. */
432 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown command.");
434 if (vsf_log_entry_pending(p_sess
))
436 vsf_log_do_log(p_sess
, 0);
442 handle_pwd(struct vsf_session
* p_sess
)
444 static struct mystr s_cwd_buf_mangle_str
;
445 static struct mystr s_pwd_res_str
;
446 str_getcwd(&s_cwd_buf_mangle_str
);
447 /* Double up any double-quotes in the pathname! */
448 str_replace_text(&s_cwd_buf_mangle_str
, "\"", "\"\"");
449 /* Enclose pathname in quotes */
450 str_alloc_text(&s_pwd_res_str
, "\"");
451 str_append_str(&s_pwd_res_str
, &s_cwd_buf_mangle_str
);
452 str_append_text(&s_pwd_res_str
, "\"");
453 vsf_cmdio_write_str(p_sess
, FTP_PWDOK
, &s_pwd_res_str
);
457 handle_cwd(struct vsf_session
* p_sess
)
460 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
461 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
463 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
466 retval
= str_chdir(&p_sess
->ftp_arg_str
);
469 /* Handle any messages */
470 vsf_banner_dir_changed(p_sess
, FTP_CWDOK
);
471 vsf_cmdio_write(p_sess
, FTP_CWDOK
, "Directory successfully changed.");
475 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to change directory.");
480 handle_cdup(struct vsf_session
* p_sess
)
482 str_alloc_text(&p_sess
->ftp_arg_str
, "..");
487 port_active(struct vsf_session
* p_sess
)
490 if (p_sess
->p_port_sockaddr
!= 0)
493 if (pasv_active(p_sess
))
495 bug("port and pasv both active");
502 pasv_active(struct vsf_session
* p_sess
)
505 if (tunable_one_process_model
)
507 ret
= vsf_one_process_pasv_active(p_sess
);
511 ret
= vsf_two_process_pasv_active(p_sess
);
515 if (port_active(p_sess
))
517 bug("pasv and port both active");
524 port_cleanup(struct vsf_session
* p_sess
)
526 vsf_sysutil_sockaddr_clear(&p_sess
->p_port_sockaddr
);
530 pasv_cleanup(struct vsf_session
* p_sess
)
532 if (tunable_one_process_model
)
534 vsf_one_process_pasv_cleanup(p_sess
);
538 vsf_two_process_pasv_cleanup(p_sess
);
543 handle_pasv(struct vsf_session
* p_sess
, int is_epsv
)
545 unsigned short the_port
;
546 static struct mystr s_pasv_res_str
;
547 static struct vsf_sysutil_sockaddr
* s_p_sockaddr
;
548 int is_ipv6
= vsf_sysutil_sockaddr_is_ipv6(p_sess
->p_local_addr
);
549 if (is_epsv
&& !str_isempty(&p_sess
->ftp_arg_str
))
552 str_upper(&p_sess
->ftp_arg_str
);
553 if (str_equal_text(&p_sess
->ftp_arg_str
, "ALL"))
555 p_sess
->epsv_all
= 1;
556 vsf_cmdio_write(p_sess
, FTP_EPSVALLOK
, "EPSV ALL ok.");
559 argval
= vsf_sysutil_atoi(str_getbuf(&p_sess
->ftp_arg_str
));
560 if (argval
< 1 || argval
> 2 || (!is_ipv6
&& argval
== 2))
562 vsf_cmdio_write(p_sess
, FTP_EPSVBAD
, "Bad network protocol.");
566 pasv_cleanup(p_sess
);
567 port_cleanup(p_sess
);
568 if (tunable_one_process_model
)
570 the_port
= vsf_one_process_listen(p_sess
);
574 the_port
= vsf_two_process_listen(p_sess
);
578 str_alloc_text(&s_pasv_res_str
, "Entering Extended Passive Mode (|||");
579 str_append_ulong(&s_pasv_res_str
, (unsigned long) the_port
);
580 str_append_text(&s_pasv_res_str
, "|).");
581 vsf_cmdio_write_str(p_sess
, FTP_EPSVOK
, &s_pasv_res_str
);
584 if (tunable_pasv_address
!= 0)
586 vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr
);
587 /* Report passive address as specified in configuration */
588 if (vsf_sysutil_inet_aton(tunable_pasv_address
, s_p_sockaddr
) == 0)
590 die("invalid pasv_address");
595 vsf_sysutil_sockaddr_clone(&s_p_sockaddr
, p_sess
->p_local_addr
);
597 str_alloc_text(&s_pasv_res_str
, "Entering Passive Mode (");
600 str_append_text(&s_pasv_res_str
, vsf_sysutil_inet_ntop(s_p_sockaddr
));
604 const void* p_v4addr
= vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr
);
607 str_append_text(&s_pasv_res_str
, vsf_sysutil_inet_ntoa(p_v4addr
));
611 str_append_text(&s_pasv_res_str
, "0,0,0,0");
614 str_replace_char(&s_pasv_res_str
, '.', ',');
615 str_append_text(&s_pasv_res_str
, ",");
616 str_append_ulong(&s_pasv_res_str
, the_port
>> 8);
617 str_append_text(&s_pasv_res_str
, ",");
618 str_append_ulong(&s_pasv_res_str
, the_port
& 255);
619 str_append_text(&s_pasv_res_str
, ").");
620 vsf_cmdio_write_str(p_sess
, FTP_PASVOK
, &s_pasv_res_str
);
624 handle_retr(struct vsf_session
* p_sess
)
626 static struct mystr s_mark_str
;
627 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
628 struct vsf_transfer_ret trans_ret
;
632 filesize_t offset
= p_sess
->restart_pos
;
633 p_sess
->restart_pos
= 0;
634 if (!data_transfer_checks_ok(p_sess
))
638 if (p_sess
->is_ascii
&& offset
!= 0)
640 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
641 "No support for resume of ASCII transfer.");
644 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
645 vsf_log_start_entry(p_sess
, kVSFLogEntryDownload
);
646 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
647 prepend_path_to_filename(&p_sess
->log_str
);
648 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
650 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
653 opened_file
= str_open(&p_sess
->ftp_arg_str
, kVSFSysStrOpenReadOnly
);
654 if (vsf_sysutil_retval_is_error(opened_file
))
656 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
659 /* Lock file if required */
660 if (tunable_lock_upload_files
)
662 vsf_sysutil_lock_file_read(opened_file
);
664 vsf_sysutil_fstat(opened_file
, &s_p_statbuf
);
665 /* No games please */
666 if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
668 /* Note - pretend open failed */
669 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
670 /* Irritating FireFox does RETR on directories, so avoid logging this
671 * very common and noisy case.
673 if (vsf_sysutil_statbuf_is_dir(s_p_statbuf
))
675 vsf_log_clear_entry(p_sess
);
679 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
682 vsf_sysutil_deactivate_noblock(opened_file
);
683 /* Optionally, we'll be paranoid and only serve publicly readable stuff */
684 if (p_sess
->is_anonymous
&& tunable_anon_world_readable_only
&&
685 !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf
))
687 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Failed to open file.");
690 /* Set the download offset (from REST) if any */
693 vsf_sysutil_lseek_to(opened_file
, offset
);
695 str_alloc_text(&s_mark_str
, "Opening ");
696 if (tunable_ascii_download_enable
&& p_sess
->is_ascii
)
698 str_append_text(&s_mark_str
, "ASCII");
703 str_append_text(&s_mark_str
, "BINARY");
705 str_append_text(&s_mark_str
, " mode data connection for ");
706 str_append_str(&s_mark_str
, &p_sess
->ftp_arg_str
);
707 str_append_text(&s_mark_str
, " (");
708 str_append_filesize_t(&s_mark_str
,
709 vsf_sysutil_statbuf_get_size(s_p_statbuf
));
710 str_append_text(&s_mark_str
, " bytes).");
711 remote_fd
= get_remote_transfer_fd(p_sess
, str_getbuf(&s_mark_str
));
712 if (vsf_sysutil_retval_is_error(remote_fd
))
714 goto port_pasv_cleanup_out
;
716 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
717 opened_file
, 0, is_ascii
);
718 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && trans_ret
.retval
== 0)
720 trans_ret
.retval
= -2;
722 p_sess
->transfer_size
= trans_ret
.transferred
;
723 /* Log _after_ the blocking dispose call, so we get transfer times right */
724 if (trans_ret
.retval
== 0)
726 vsf_log_do_log(p_sess
, 1);
728 /* Emit status message _after_ blocking dispose call to avoid buggy FTP
729 * clients truncating the transfer.
731 if (trans_ret
.retval
== -1)
733 vsf_cmdio_write(p_sess
, FTP_BADSENDFILE
, "Failure reading local file.");
735 else if (trans_ret
.retval
== -2)
737 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
, "Failure writing network stream.");
741 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Transfer complete.");
744 port_pasv_cleanup_out
:
745 port_cleanup(p_sess
);
746 pasv_cleanup(p_sess
);
748 vsf_sysutil_close(opened_file
);
752 handle_list(struct vsf_session
* p_sess
)
754 handle_dir_common(p_sess
, 1, 0);
758 handle_dir_common(struct vsf_session
* p_sess
, int full_details
, int stat_cmd
)
760 static struct mystr s_option_str
;
761 static struct mystr s_filter_str
;
762 static struct mystr s_dir_name_str
;
763 static struct vsf_sysutil_statbuf
* s_p_dirstat
;
764 int dir_allow_read
= 1;
765 struct vsf_sysutil_dir
* p_dir
= 0;
768 str_empty(&s_option_str
);
769 str_empty(&s_filter_str
);
770 /* By default open the current directory */
771 str_alloc_text(&s_dir_name_str
, ".");
772 if (!stat_cmd
&& !data_transfer_checks_ok(p_sess
))
776 /* Do we have an option? Going to be strict here - the option must come
777 * first. e.g. "ls -a .." fine, "ls .. -a" not fine
779 if (!str_isempty(&p_sess
->ftp_arg_str
) &&
780 str_get_char_at(&p_sess
->ftp_arg_str
, 0) == '-')
782 /* Chop off the '-' */
783 str_mid_to_end(&p_sess
->ftp_arg_str
, &s_option_str
, 1);
784 /* A space will separate options from filter (if any) */
785 str_split_char(&s_option_str
, &s_filter_str
, ' ');
789 /* The argument, if any, is just a filter */
790 str_copy(&s_filter_str
, &p_sess
->ftp_arg_str
);
792 if (!str_isempty(&s_filter_str
))
794 resolve_tilde(&s_filter_str
, p_sess
);
795 if (!vsf_access_check_file(&s_filter_str
))
797 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
800 /* First check - is it an outright directory, as in "ls /pub" */
801 p_dir
= str_opendir(&s_filter_str
);
804 /* Listing a directory! */
805 str_copy(&s_dir_name_str
, &s_filter_str
);
806 str_free(&s_filter_str
);
810 struct str_locate_result locate_result
=
811 str_locate_char(&s_filter_str
, '/');
812 if (locate_result
.found
)
814 /* Includes a path! Reverse scan for / in the arg, to get the
815 * base directory and filter (if any)
817 str_copy(&s_dir_name_str
, &s_filter_str
);
818 str_split_char_reverse(&s_dir_name_str
, &s_filter_str
, '/');
819 /* If we have e.g. "ls /.message", we just ripped off the leading
820 * slash because it is the only one!
822 if (str_isempty(&s_dir_name_str
))
824 str_alloc_text(&s_dir_name_str
, "/");
831 /* NOTE - failure check done below, it's not forgotten */
832 p_dir
= str_opendir(&s_dir_name_str
);
838 str_append_char(&s_option_str
, 'a');
839 vsf_cmdio_write_hyphen(p_sess
, FTP_STATFILE_OK
, "Status follows:");
843 int remote_fd
= get_remote_transfer_fd(
844 p_sess
, "Here comes the directory listing.");
845 if (vsf_sysutil_retval_is_error(remote_fd
))
850 if (p_sess
->is_anonymous
&& p_dir
&& tunable_anon_world_readable_only
)
852 vsf_sysutil_dir_stat(p_dir
, &s_p_dirstat
);
853 if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat
))
858 if (p_dir
!= 0 && dir_allow_read
)
860 retval
= vsf_ftpdataio_transfer_dir(p_sess
, use_control
, p_dir
,
861 &s_dir_name_str
, &s_option_str
,
862 &s_filter_str
, full_details
);
866 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && retval
== 0)
873 vsf_cmdio_write(p_sess
, FTP_STATFILE_OK
, "End of status");
875 else if (p_dir
== 0 || !dir_allow_read
)
877 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
,
878 "Transfer done (but failed to open directory).");
880 else if (retval
== 0)
882 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Directory send OK.");
886 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
, "Failure writing network stream.");
892 vsf_sysutil_closedir(p_dir
);
896 port_cleanup(p_sess
);
897 pasv_cleanup(p_sess
);
902 handle_type(struct vsf_session
* p_sess
)
904 str_upper(&p_sess
->ftp_arg_str
);
905 if (str_equal_text(&p_sess
->ftp_arg_str
, "I") ||
906 str_equal_text(&p_sess
->ftp_arg_str
, "L8") ||
907 str_equal_text(&p_sess
->ftp_arg_str
, "L 8"))
909 p_sess
->is_ascii
= 0;
910 vsf_cmdio_write(p_sess
, FTP_TYPEOK
, "Switching to Binary mode.");
912 else if (str_equal_text(&p_sess
->ftp_arg_str
, "A") ||
913 str_equal_text(&p_sess
->ftp_arg_str
, "A N"))
915 p_sess
->is_ascii
= 1;
916 vsf_cmdio_write(p_sess
, FTP_TYPEOK
, "Switching to ASCII mode.");
920 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unrecognised TYPE command.");
925 handle_port(struct vsf_session
* p_sess
)
927 unsigned short the_port
;
928 unsigned char vals
[6];
929 const unsigned char* p_raw
;
930 pasv_cleanup(p_sess
);
931 port_cleanup(p_sess
);
932 p_raw
= vsf_sysutil_parse_uchar_string_sep(&p_sess
->ftp_arg_str
, ',', vals
,
936 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal PORT command.");
939 the_port
= vals
[4] << 8;
941 vsf_sysutil_sockaddr_clone(&p_sess
->p_port_sockaddr
, p_sess
->p_local_addr
);
942 vsf_sysutil_sockaddr_set_ipv4addr(p_sess
->p_port_sockaddr
, vals
);
943 vsf_sysutil_sockaddr_set_port(p_sess
->p_port_sockaddr
, the_port
);
945 * 1) Reject requests not connecting to the control socket IP
946 * 2) Reject connects to privileged ports
948 if (!tunable_port_promiscuous
)
950 if (!vsf_sysutil_sockaddr_addr_equal(p_sess
->p_remote_addr
,
951 p_sess
->p_port_sockaddr
) ||
952 vsf_sysutil_is_port_reserved(the_port
))
954 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal PORT command.");
955 port_cleanup(p_sess
);
959 vsf_cmdio_write(p_sess
, FTP_PORTOK
,
960 "PORT command successful. Consider using PASV.");
964 handle_stor(struct vsf_session
* p_sess
)
966 handle_upload_common(p_sess
, 0, 0);
970 handle_upload_common(struct vsf_session
* p_sess
, int is_append
, int is_unique
)
972 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
973 static struct mystr s_filename
;
974 struct mystr
* p_filename
;
975 struct vsf_transfer_ret trans_ret
;
981 filesize_t offset
= p_sess
->restart_pos
;
982 p_sess
->restart_pos
= 0;
983 if (!data_transfer_checks_ok(p_sess
))
987 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
988 p_filename
= &p_sess
->ftp_arg_str
;
991 get_unique_filename(&s_filename
, p_filename
);
992 p_filename
= &s_filename
;
994 vsf_log_start_entry(p_sess
, kVSFLogEntryUpload
);
995 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
996 prepend_path_to_filename(&p_sess
->log_str
);
997 if (!vsf_access_check_file(p_filename
))
999 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1002 /* NOTE - actual file permissions will be governed by the tunable umask */
1003 /* XXX - do we care about race between create and chown() of anonymous
1006 if (is_unique
|| (p_sess
->is_anonymous
&& !tunable_anon_other_write_enable
))
1008 new_file_fd
= str_create(p_filename
);
1012 /* For non-anonymous, allow open() to overwrite or append existing files */
1013 new_file_fd
= str_create_append(p_filename
);
1014 if (!is_append
&& offset
== 0)
1019 if (vsf_sysutil_retval_is_error(new_file_fd
))
1021 vsf_cmdio_write(p_sess
, FTP_UPLOADFAIL
, "Could not create file.");
1025 vsf_sysutil_fstat(new_file_fd
, &s_p_statbuf
);
1026 if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1028 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
1029 * such as XFS DMAPI.
1031 vsf_sysutil_deactivate_noblock(new_file_fd
);
1033 /* Are we required to chown() this file for security? */
1034 if (p_sess
->is_anonymous
&& tunable_chown_uploads
)
1036 vsf_sysutil_fchmod(new_file_fd
, tunable_chown_upload_mode
);
1037 if (tunable_one_process_model
)
1039 vsf_one_process_chown_upload(p_sess
, new_file_fd
);
1043 vsf_two_process_chown_upload(p_sess
, new_file_fd
);
1046 /* Are we required to lock this file? */
1047 if (tunable_lock_upload_files
)
1049 vsf_sysutil_lock_file_write(new_file_fd
);
1051 /* Must truncate the file AFTER locking it! */
1054 vsf_sysutil_ftruncate(new_file_fd
);
1055 vsf_sysutil_lseek_to(new_file_fd
, 0);
1057 if (!is_append
&& offset
!= 0)
1059 /* XXX - warning, allows seek past end of file! Check for seek > size? */
1060 /* XXX - also, currently broken as the O_APPEND flag will always write
1061 * at the end of file. No known complaints yet; can easily fix if one
1064 vsf_sysutil_lseek_to(new_file_fd
, offset
);
1068 struct mystr resp_str
= INIT_MYSTR
;
1069 str_alloc_text(&resp_str
, "FILE: ");
1070 str_append_str(&resp_str
, p_filename
);
1071 remote_fd
= get_remote_transfer_fd(p_sess
, str_getbuf(&resp_str
));
1072 str_free(&resp_str
);
1076 remote_fd
= get_remote_transfer_fd(p_sess
, "Ok to send data.");
1078 if (vsf_sysutil_retval_is_error(remote_fd
))
1080 goto port_pasv_cleanup_out
;
1082 if (tunable_ascii_upload_enable
&& p_sess
->is_ascii
)
1084 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
1089 trans_ret
= vsf_ftpdataio_transfer_file(p_sess
, remote_fd
,
1092 if (vsf_ftpdataio_dispose_transfer_fd(p_sess
) != 1 && trans_ret
.retval
== 0)
1094 trans_ret
.retval
= -2;
1096 p_sess
->transfer_size
= trans_ret
.transferred
;
1097 if (trans_ret
.retval
== 0)
1100 vsf_log_do_log(p_sess
, 1);
1102 if (trans_ret
.retval
== -1)
1104 vsf_cmdio_write(p_sess
, FTP_BADSENDFILE
, "Failure writing to local file.");
1106 else if (trans_ret
.retval
== -2 || p_sess
->abor_received
)
1108 vsf_cmdio_write(p_sess
, FTP_BADSENDNET
, "Failure reading network stream.");
1112 vsf_cmdio_write(p_sess
, FTP_TRANSFEROK
, "Transfer complete.");
1115 port_pasv_cleanup_out
:
1116 port_cleanup(p_sess
);
1117 pasv_cleanup(p_sess
);
1118 if (tunable_delete_failed_uploads
&& created
&& !success
)
1120 str_unlink(p_filename
);
1122 vsf_sysutil_close(new_file_fd
);
1126 handle_mkd(struct vsf_session
* p_sess
)
1129 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1130 vsf_log_start_entry(p_sess
, kVSFLogEntryMkdir
);
1131 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1132 prepend_path_to_filename(&p_sess
->log_str
);
1133 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1135 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1138 /* NOTE! Actual permissions will be governed by the tunable umask */
1139 retval
= str_mkdir(&p_sess
->ftp_arg_str
, 0777);
1142 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1143 "Create directory operation failed.");
1146 vsf_log_do_log(p_sess
, 1);
1148 static struct mystr s_mkd_res
;
1149 static struct mystr s_tmp_str
;
1150 str_copy(&s_tmp_str
, &p_sess
->ftp_arg_str
);
1151 prepend_path_to_filename(&s_tmp_str
);
1152 /* Double up double quotes */
1153 str_replace_text(&s_tmp_str
, "\"", "\"\"");
1154 /* Build result string */
1155 str_alloc_text(&s_mkd_res
, "\"");
1156 str_append_str(&s_mkd_res
, &s_tmp_str
);
1157 str_append_text(&s_mkd_res
, "\" created");
1158 vsf_cmdio_write_str(p_sess
, FTP_MKDIROK
, &s_mkd_res
);
1163 handle_rmd(struct vsf_session
* p_sess
)
1166 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1167 vsf_log_start_entry(p_sess
, kVSFLogEntryRmdir
);
1168 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1169 prepend_path_to_filename(&p_sess
->log_str
);
1170 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1172 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1175 retval
= str_rmdir(&p_sess
->ftp_arg_str
);
1178 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1179 "Remove directory operation failed.");
1183 vsf_log_do_log(p_sess
, 1);
1184 vsf_cmdio_write(p_sess
, FTP_RMDIROK
,
1185 "Remove directory operation successful.");
1190 handle_dele(struct vsf_session
* p_sess
)
1193 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1194 vsf_log_start_entry(p_sess
, kVSFLogEntryDelete
);
1195 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1196 prepend_path_to_filename(&p_sess
->log_str
);
1197 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1199 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1202 retval
= str_unlink(&p_sess
->ftp_arg_str
);
1205 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Delete operation failed.");
1209 vsf_log_do_log(p_sess
, 1);
1210 vsf_cmdio_write(p_sess
, FTP_DELEOK
, "Delete operation successful.");
1215 handle_rest(struct vsf_session
* p_sess
)
1217 static struct mystr s_rest_str
;
1218 filesize_t val
= str_a_to_filesize_t(&p_sess
->ftp_arg_str
);
1223 p_sess
->restart_pos
= val
;
1224 str_alloc_text(&s_rest_str
, "Restart position accepted (");
1225 str_append_filesize_t(&s_rest_str
, val
);
1226 str_append_text(&s_rest_str
, ").");
1227 vsf_cmdio_write_str(p_sess
, FTP_RESTOK
, &s_rest_str
);
1231 handle_rnfr(struct vsf_session
* p_sess
)
1233 static struct vsf_sysutil_statbuf
* p_statbuf
;
1235 /* Clear old value */
1236 str_free(&p_sess
->rnfr_filename_str
);
1237 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1238 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1240 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1241 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1242 prepend_path_to_filename(&p_sess
->log_str
);
1243 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1246 /* Does it exist? */
1247 retval
= str_stat(&p_sess
->ftp_arg_str
, &p_statbuf
);
1251 str_copy(&p_sess
->rnfr_filename_str
, &p_sess
->ftp_arg_str
);
1252 vsf_cmdio_write(p_sess
, FTP_RNFROK
, "Ready for RNTO.");
1256 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1257 str_copy(&p_sess
->log_str
, &p_sess
->ftp_arg_str
);
1258 prepend_path_to_filename(&p_sess
->log_str
);
1259 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "RNFR command failed.");
1264 handle_rnto(struct vsf_session
* p_sess
)
1266 static struct mystr s_tmp_str
;
1268 /* If we didn't get a RNFR, throw a wobbly */
1269 if (str_isempty(&p_sess
->rnfr_filename_str
))
1271 vsf_cmdio_write(p_sess
, FTP_NEEDRNFR
,
1272 "RNFR required first.");
1275 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1276 vsf_log_start_entry(p_sess
, kVSFLogEntryRename
);
1277 str_copy(&p_sess
->log_str
, &p_sess
->rnfr_filename_str
);
1278 prepend_path_to_filename(&p_sess
->log_str
);
1279 str_append_char(&p_sess
->log_str
, ' ');
1280 str_copy(&s_tmp_str
, &p_sess
->ftp_arg_str
);
1281 prepend_path_to_filename(&s_tmp_str
);
1282 str_append_str(&p_sess
->log_str
, &s_tmp_str
);
1283 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1285 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1288 /* NOTE - might overwrite destination file. Not a concern because the same
1289 * could be accomplished with DELE.
1291 retval
= str_rename(&p_sess
->rnfr_filename_str
, &p_sess
->ftp_arg_str
);
1292 /* Clear the RNFR filename; start the two stage process again! */
1293 str_free(&p_sess
->rnfr_filename_str
);
1296 vsf_log_do_log(p_sess
, 1);
1297 vsf_cmdio_write(p_sess
, FTP_RENAMEOK
, "Rename successful.");
1301 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Rename failed.");
1306 handle_nlst(struct vsf_session
* p_sess
)
1308 handle_dir_common(p_sess
, 0, 0);
1312 prepend_path_to_filename(struct mystr
* p_str
)
1314 static struct mystr s_tmp_str
;
1315 /* Only prepend current working directory if the incoming filename is
1318 str_empty(&s_tmp_str
);
1319 if (str_isempty(p_str
) || str_get_char_at(p_str
, 0) != '/')
1321 str_getcwd(&s_tmp_str
);
1322 /* Careful to not emit // if we are in directory / (common with chroot) */
1323 if (str_isempty(&s_tmp_str
) ||
1324 str_get_char_at(&s_tmp_str
, str_getlen(&s_tmp_str
) - 1) != '/')
1326 str_append_char(&s_tmp_str
, '/');
1329 str_append_str(&s_tmp_str
, p_str
);
1330 str_copy(p_str
, &s_tmp_str
);
1335 handle_sigurg(void* p_private
)
1337 struct mystr async_cmd_str
= INIT_MYSTR
;
1338 struct mystr async_arg_str
= INIT_MYSTR
;
1339 struct mystr real_cmd_str
= INIT_MYSTR
;
1341 struct vsf_session
* p_sess
= (struct vsf_session
*) p_private
;
1342 /* Did stupid client sent something OOB without a data connection? */
1343 if (p_sess
->data_fd
== -1)
1347 /* Get the async command - blocks (use data timeout alarm) */
1348 vsf_cmdio_get_cmd_and_arg(p_sess
, &async_cmd_str
, &async_arg_str
, 0);
1349 /* Chop off first four characters; they are telnet characters. The client
1350 * should have sent the first two normally and the second two as urgent
1353 len
= str_getlen(&async_cmd_str
);
1356 str_right(&async_cmd_str
, &real_cmd_str
, len
- 4);
1358 if (str_equal_text(&real_cmd_str
, "ABOR"))
1360 p_sess
->abor_received
= 1;
1361 /* This is failok because of a small race condition; the SIGURG might
1362 * be raised after the data socket is closed, but before data_fd is
1365 vsf_sysutil_shutdown_failok(p_sess
->data_fd
);
1370 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown command.");
1372 str_free(&async_cmd_str
);
1373 str_free(&async_arg_str
);
1374 str_free(&real_cmd_str
);
1378 get_remote_transfer_fd(struct vsf_session
* p_sess
, const char* p_status_msg
)
1381 if (!pasv_active(p_sess
) && !port_active(p_sess
))
1383 bug("neither PORT nor PASV active in get_remote_transfer_fd");
1385 p_sess
->abor_received
= 0;
1386 if (pasv_active(p_sess
))
1388 remote_fd
= vsf_ftpdataio_get_pasv_fd(p_sess
);
1392 remote_fd
= vsf_ftpdataio_get_port_fd(p_sess
);
1394 if (vsf_sysutil_retval_is_error(remote_fd
))
1398 vsf_cmdio_write(p_sess
, FTP_DATACONN
, p_status_msg
);
1399 if (vsf_ftpdataio_post_mark_connect(p_sess
) != 1)
1401 vsf_ftpdataio_dispose_transfer_fd(p_sess
);
1408 check_abor(struct vsf_session
* p_sess
)
1410 /* If the client sent ABOR, respond to it here */
1411 if (p_sess
->abor_received
)
1413 p_sess
->abor_received
= 0;
1414 vsf_cmdio_write(p_sess
, FTP_ABOROK
, "ABOR successful.");
1419 handle_size(struct vsf_session
* p_sess
)
1421 /* Note - in ASCII mode, are supposed to return the size after taking into
1422 * account ASCII linefeed conversions. At least this is what wu-ftpd does in
1423 * version 2.6.1. Proftpd-1.2.0pre fails to do this.
1424 * I will not do it because it is a potential I/O DoS.
1426 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1428 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1429 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1431 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1434 retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1435 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1437 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "Could not get file size.");
1441 static struct mystr s_size_res_str
;
1442 str_alloc_filesize_t(&s_size_res_str
,
1443 vsf_sysutil_statbuf_get_size(s_p_statbuf
));
1444 vsf_cmdio_write_str(p_sess
, FTP_SIZEOK
, &s_size_res_str
);
1449 handle_site(struct vsf_session
* p_sess
)
1451 static struct mystr s_site_args_str
;
1452 /* What SITE sub-command is it? */
1453 str_split_char(&p_sess
->ftp_arg_str
, &s_site_args_str
, ' ');
1454 str_upper(&p_sess
->ftp_arg_str
);
1455 if (tunable_write_enable
&&
1456 tunable_chmod_enable
&&
1457 str_equal_text(&p_sess
->ftp_arg_str
, "CHMOD"))
1459 handle_site_chmod(p_sess
, &s_site_args_str
);
1461 else if (str_equal_text(&p_sess
->ftp_arg_str
, "UMASK"))
1463 handle_site_umask(p_sess
, &s_site_args_str
);
1465 else if (str_equal_text(&p_sess
->ftp_arg_str
, "HELP"))
1467 vsf_cmdio_write(p_sess
, FTP_SITEHELP
, "CHMOD UMASK HELP");
1471 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Unknown SITE command.");
1476 handle_site_chmod(struct vsf_session
* p_sess
, struct mystr
* p_arg_str
)
1478 static struct mystr s_chmod_file_str
;
1481 if (str_isempty(p_arg_str
))
1483 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "SITE CHMOD needs 2 arguments.");
1486 str_split_char(p_arg_str
, &s_chmod_file_str
, ' ');
1487 if (str_isempty(&s_chmod_file_str
))
1489 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "SITE CHMOD needs 2 arguments.");
1492 resolve_tilde(&s_chmod_file_str
, p_sess
);
1493 vsf_log_start_entry(p_sess
, kVSFLogEntryChmod
);
1494 str_copy(&p_sess
->log_str
, &s_chmod_file_str
);
1495 prepend_path_to_filename(&p_sess
->log_str
);
1496 str_append_char(&p_sess
->log_str
, ' ');
1497 str_append_str(&p_sess
->log_str
, p_arg_str
);
1498 if (!vsf_access_check_file(&s_chmod_file_str
))
1500 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1503 /* Don't worry - our chmod() implementation only allows 0 - 0777 */
1504 perms
= str_octal_to_uint(p_arg_str
);
1505 retval
= str_chmod(&s_chmod_file_str
, perms
);
1506 if (vsf_sysutil_retval_is_error(retval
))
1508 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
, "SITE CHMOD command failed.");
1512 vsf_log_do_log(p_sess
, 1);
1513 vsf_cmdio_write(p_sess
, FTP_CHMODOK
, "SITE CHMOD command ok.");
1518 handle_site_umask(struct vsf_session
* p_sess
, struct mystr
* p_arg_str
)
1520 static struct mystr s_umask_resp_str
;
1521 if (str_isempty(p_arg_str
))
1523 /* Empty arg => report current umask */
1524 str_alloc_text(&s_umask_resp_str
, "Your current UMASK is ");
1525 str_append_text(&s_umask_resp_str
,
1526 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1530 /* Set current umask */
1531 unsigned int new_umask
= str_octal_to_uint(p_arg_str
);
1532 vsf_sysutil_set_umask(new_umask
);
1533 str_alloc_text(&s_umask_resp_str
, "UMASK set to ");
1534 str_append_text(&s_umask_resp_str
,
1535 vsf_sysutil_uint_to_octal(vsf_sysutil_get_umask()));
1537 vsf_cmdio_write_str(p_sess
, FTP_UMASKOK
, &s_umask_resp_str
);
1541 handle_appe(struct vsf_session
* p_sess
)
1543 handle_upload_common(p_sess
, 1, 0);
1547 handle_mdtm(struct vsf_session
* p_sess
)
1549 static struct mystr s_filename_str
;
1550 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1553 struct str_locate_result loc
= str_locate_char(&p_sess
->ftp_arg_str
, ' ');
1554 int retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1555 if (tunable_mdtm_write
&& retval
!= 0 && loc
.found
&&
1556 vsf_sysutil_isdigit(str_get_char_at(&p_sess
->ftp_arg_str
, 0)))
1558 if (loc
.index
== 8 || loc
.index
== 14 ||
1559 (loc
.index
> 15 && str_get_char_at(&p_sess
->ftp_arg_str
, 14) == '.'))
1566 str_split_char(&p_sess
->ftp_arg_str
, &s_filename_str
, ' ');
1567 modtime
= vsf_sysutil_parse_time(str_getbuf(&p_sess
->ftp_arg_str
));
1568 str_copy(&p_sess
->ftp_arg_str
, &s_filename_str
);
1570 resolve_tilde(&p_sess
->ftp_arg_str
, p_sess
);
1571 if (!vsf_access_check_file(&p_sess
->ftp_arg_str
))
1573 vsf_cmdio_write(p_sess
, FTP_NOPERM
, "Permission denied.");
1576 if (do_write
&& tunable_write_enable
&&
1577 (tunable_anon_other_write_enable
|| !p_sess
->is_anonymous
))
1579 retval
= str_stat(&p_sess
->ftp_arg_str
, &s_p_statbuf
);
1580 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1582 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1583 "Could not set file modification time.");
1587 retval
= vsf_sysutil_setmodtime(
1588 str_getbuf(&p_sess
->ftp_arg_str
), modtime
, tunable_use_localtime
);
1591 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1592 "Could not set file modification time.");
1596 vsf_cmdio_write(p_sess
, FTP_MDTMOK
,
1597 "File modification time set.");
1603 if (retval
!= 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf
))
1605 vsf_cmdio_write(p_sess
, FTP_FILEFAIL
,
1606 "Could not get file modification time.");
1610 static struct mystr s_mdtm_res_str
;
1611 str_alloc_text(&s_mdtm_res_str
,
1612 vsf_sysutil_statbuf_get_numeric_date(
1613 s_p_statbuf
, tunable_use_localtime
));
1614 vsf_cmdio_write_str(p_sess
, FTP_MDTMOK
, &s_mdtm_res_str
);
1620 handle_eprt(struct vsf_session
* p_sess
)
1622 static struct mystr s_part1_str
;
1623 static struct mystr s_part2_str
;
1624 static struct mystr s_scopeid_str
;
1627 const unsigned char* p_raw_addr
;
1628 int is_ipv6
= vsf_sysutil_sockaddr_is_ipv6(p_sess
->p_local_addr
);
1629 port_cleanup(p_sess
);
1630 pasv_cleanup(p_sess
);
1631 str_copy(&s_part1_str
, &p_sess
->ftp_arg_str
);
1632 str_split_char(&s_part1_str
, &s_part2_str
, '|');
1633 if (!str_isempty(&s_part1_str
))
1637 /* Split out the protocol and check it */
1638 str_split_char(&s_part2_str
, &s_part1_str
, '|');
1639 proto
= str_atoi(&s_part2_str
);
1640 if (proto
< 1 || proto
> 2 || (!is_ipv6
&& proto
== 2))
1642 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Bad EPRT protocol.");
1645 /* Split out address and parse it */
1646 str_split_char(&s_part1_str
, &s_part2_str
, '|');
1649 str_split_char(&s_part1_str
, &s_scopeid_str
, '%');
1650 p_raw_addr
= vsf_sysutil_parse_ipv6(&s_part1_str
);
1654 p_raw_addr
= vsf_sysutil_parse_ipv4(&s_part1_str
);
1660 /* Split out port and parse it */
1661 str_split_char(&s_part2_str
, &s_part1_str
, '|');
1662 if (!str_isempty(&s_part1_str
) || str_isempty(&s_part2_str
))
1666 port
= str_atoi(&s_part2_str
);
1667 if (port
< 0 || port
> 65535)
1671 vsf_sysutil_sockaddr_clone(&p_sess
->p_port_sockaddr
, p_sess
->p_local_addr
);
1674 vsf_sysutil_sockaddr_set_ipv6addr(p_sess
->p_port_sockaddr
, p_raw_addr
);
1678 vsf_sysutil_sockaddr_set_ipv4addr(p_sess
->p_port_sockaddr
, p_raw_addr
);
1680 vsf_sysutil_sockaddr_set_port(p_sess
->p_port_sockaddr
, (unsigned short) port
);
1682 * 1) Reject requests not connecting to the control socket IP
1683 * 2) Reject connects to privileged ports
1685 if (!tunable_port_promiscuous
)
1687 if (!vsf_sysutil_sockaddr_addr_equal(p_sess
->p_remote_addr
,
1688 p_sess
->p_port_sockaddr
) ||
1689 vsf_sysutil_is_port_reserved(port
))
1691 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Illegal EPRT command.");
1692 port_cleanup(p_sess
);
1696 vsf_cmdio_write(p_sess
, FTP_EPRTOK
,
1697 "EPRT command successful. Consider using EPSV.");
1700 vsf_cmdio_write(p_sess
, FTP_BADCMD
, "Bad EPRT command.");
1703 /* XXX - add AUTH etc. */
1705 handle_help(struct vsf_session
* p_sess
)
1707 vsf_cmdio_write_hyphen(p_sess
, FTP_HELP
,
1708 "The following commands are recognized.");
1709 vsf_cmdio_write_raw(p_sess
,
1710 " ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n");
1711 vsf_cmdio_write_raw(p_sess
,
1712 " MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n");
1713 vsf_cmdio_write_raw(p_sess
,
1714 " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
1715 vsf_cmdio_write_raw(p_sess
,
1717 vsf_cmdio_write(p_sess
, FTP_HELP
, "Help OK.");
1721 handle_stou(struct vsf_session
* p_sess
)
1723 handle_upload_common(p_sess
, 0, 1);
1727 get_unique_filename(struct mystr
* p_outstr
, const struct mystr
* p_base_str
)
1729 /* Use silly wu-ftpd algorithm for compatibility. It has races of course, if
1730 * two sessions are using the same file prefix at the same time.
1732 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
1733 unsigned int suffix
= 1;
1734 /* Do not add any suffix at all if the name is not taken. */
1735 int retval
= str_stat(p_base_str
, &s_p_statbuf
);
1736 if (vsf_sysutil_retval_is_error(retval
))
1738 str_copy(p_outstr
, p_base_str
);
1743 str_copy(p_outstr
, p_base_str
);
1744 str_append_char(p_outstr
, '.');
1745 str_append_ulong(p_outstr
, suffix
);
1746 retval
= str_stat(p_outstr
, &s_p_statbuf
);
1747 if (vsf_sysutil_retval_is_error(retval
))
1756 handle_stat(struct vsf_session
* p_sess
)
1758 vsf_cmdio_write_hyphen(p_sess
, FTP_STATOK
, "FTP server status:");
1759 vsf_cmdio_write_raw(p_sess
, " Connected to ");
1760 vsf_cmdio_write_raw(p_sess
, str_getbuf(&p_sess
->remote_ip_str
));
1761 vsf_cmdio_write_raw(p_sess
, "\r\n");
1762 vsf_cmdio_write_raw(p_sess
, " Logged in as ");
1763 vsf_cmdio_write_raw(p_sess
, str_getbuf(&p_sess
->user_str
));
1764 vsf_cmdio_write_raw(p_sess
, "\r\n");
1765 vsf_cmdio_write_raw(p_sess
, " TYPE: ");
1766 if (p_sess
->is_ascii
)
1768 vsf_cmdio_write_raw(p_sess
, "ASCII\r\n");
1772 vsf_cmdio_write_raw(p_sess
, "BINARY\r\n");
1774 if (p_sess
->bw_rate_max
== 0)
1776 vsf_cmdio_write_raw(p_sess
, " No session bandwidth limit\r\n");
1780 vsf_cmdio_write_raw(p_sess
, " Session bandwidth limit in byte/s is ");
1781 vsf_cmdio_write_raw(p_sess
, vsf_sysutil_ulong_to_str(p_sess
->bw_rate_max
));
1782 vsf_cmdio_write_raw(p_sess
, "\r\n");
1784 if (tunable_idle_session_timeout
== 0)
1786 vsf_cmdio_write_raw(p_sess
, " No session timeout\r\n");
1790 vsf_cmdio_write_raw(p_sess
, " Session timeout in seconds is ");
1791 vsf_cmdio_write_raw(p_sess
,
1792 vsf_sysutil_ulong_to_str(tunable_idle_session_timeout
));
1793 vsf_cmdio_write_raw(p_sess
, "\r\n");
1795 if (p_sess
->control_use_ssl
)
1797 vsf_cmdio_write_raw(p_sess
, " Control connection is encrypted\r\n");
1801 vsf_cmdio_write_raw(p_sess
, " Control connection is plain text\r\n");
1803 if (p_sess
->data_use_ssl
)
1805 vsf_cmdio_write_raw(p_sess
, " Data connections will be encrypted\r\n");
1809 vsf_cmdio_write_raw(p_sess
, " Data connections will be plain text\r\n");
1811 if (p_sess
->num_clients
> 0)
1813 vsf_cmdio_write_raw(p_sess
, " At session startup, client count was ");
1814 vsf_cmdio_write_raw(p_sess
, vsf_sysutil_ulong_to_str(p_sess
->num_clients
));
1815 vsf_cmdio_write_raw(p_sess
, "\r\n");
1817 vsf_cmdio_write_raw(p_sess
,
1818 " vsFTPd " VSF_VERSION
" - secure, fast, stable\r\n");
1819 vsf_cmdio_write(p_sess
, FTP_STATOK
, "End of status");
1823 handle_stat_file(struct vsf_session
* p_sess
)
1825 handle_dir_common(p_sess
, 1, 1);
1829 data_transfer_checks_ok(struct vsf_session
* p_sess
)
1831 if (!pasv_active(p_sess
) && !port_active(p_sess
))
1833 vsf_cmdio_write(p_sess
, FTP_BADSENDCONN
, "Use PORT or PASV first.");
1836 if (tunable_ssl_enable
&& !p_sess
->data_use_ssl
&&
1837 ((tunable_force_local_data_ssl
&& !p_sess
->is_anonymous
) ||
1838 (tunable_force_anon_data_ssl
&& p_sess
->is_anonymous
)))
1841 p_sess
, FTP_NEEDENCRYPT
, "Data connections must be encrypted.");
1848 resolve_tilde(struct mystr
* p_str
, struct vsf_session
* p_sess
)
1850 unsigned int len
= str_getlen(p_str
);
1851 if (len
> 0 && str_get_char_at(p_str
, 0) == '~')
1853 static struct mystr s_rhs_str
;
1854 if (len
== 1 || str_get_char_at(p_str
, 1) == '/')
1856 str_split_char(p_str
, &s_rhs_str
, '~');
1857 str_copy(p_str
, &p_sess
->home_str
);
1858 str_append_str(p_str
, &s_rhs_str
);
1860 else if (tunable_tilde_user_enable
&& len
> 1)
1862 static struct mystr s_user_str
;
1863 struct vsf_sysutil_user
* p_user
;
1864 str_copy(&s_rhs_str
, p_str
);
1865 str_split_char(&s_rhs_str
, &s_user_str
, '~');
1866 str_split_char(&s_user_str
, &s_rhs_str
, '/');
1867 p_user
= str_getpwnam(&s_user_str
);
1870 str_alloc_text(p_str
, vsf_sysutil_user_get_homedir(p_user
));
1871 if (!str_isempty(&s_rhs_str
))
1873 str_append_char(p_str
, '/');
1874 str_append_str(p_str
, &s_rhs_str
);
1881 static void handle_logged_in_user(struct vsf_session
* p_sess
)
1883 if (p_sess
->is_anonymous
)
1885 vsf_cmdio_write(p_sess
, FTP_LOGINERR
, "Can't change from guest user.");
1887 else if (str_equal(&p_sess
->user_str
, &p_sess
->ftp_arg_str
))
1889 vsf_cmdio_write(p_sess
, FTP_GIVEPWORD
, "Any password will do.");
1893 vsf_cmdio_write(p_sess
, FTP_LOGINERR
, "Can't change to another user.");
1897 static void handle_logged_in_pass(struct vsf_session
* p_sess
)
1899 vsf_cmdio_write(p_sess
, FTP_LOGINOK
, "Already logged in.");