libsodium: Needed for Dnscrypto-proxy Release 1.3.0
[tomato.git] / release / src / router / vsftpd / postlogin.c
blob8df7c0b643ba83b1bb740b9476aeff0f217c865a
1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * postlogin.c
6 */
8 #include "postlogin.h"
9 #include "session.h"
10 #include "oneprocess.h"
11 #include "twoprocess.h"
12 #include "ftpcodes.h"
13 #include "ftpcmdio.h"
14 #include "ftpdataio.h"
15 #include "utility.h"
16 #include "tunables.h"
17 #include "defs.h"
18 #include "str.h"
19 #include "sysstr.h"
20 #include "banner.h"
21 #include "sysutil.h"
22 #include "logging.h"
23 #include "sysdeputil.h"
24 #include "ipaddrparse.h"
25 #include "access.h"
26 #include "features.h"
27 #include "ssl.h"
28 #include "vsftpver.h"
29 #include "opts.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,
70 int stat_cmd);
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,
77 int is_unique);
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);
83 void
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;
92 else
94 vsf_sysutil_set_umask(tunable_local_umask);
95 p_sess->bw_rate_max = tunable_local_max_rate;
97 if (p_sess->is_http)
99 handle_http(p_sess);
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.");
112 while(1)
114 int cmd_ok = 1;
115 if (tunable_setproctitle_enable)
117 vsf_sysutil_setproctitle("IDLE");
119 /* Blocks */
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);
142 while (1)
144 str_split_char(&s_src_str, &s_rhs_str, ',');
145 if (str_isempty(&s_src_str))
147 cmd_ok = 0;
148 break;
150 else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
152 break;
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);
162 while (1)
164 str_split_char(&s_src_str, &s_rhs_str, ',');
165 if (str_isempty(&s_src_str))
167 break;
169 else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
171 cmd_ok = 0;
172 break;
174 str_copy(&s_src_str, &s_rhs_str);
177 if (!cmd_ok)
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"))
188 handle_pwd(p_sess);
190 else if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") ||
191 str_equal_text(&p_sess->ftp_cmd_str, "XCWD"))
193 handle_cwd(p_sess);
195 else if (str_equal_text(&p_sess->ftp_cmd_str, "CDUP") ||
196 str_equal_text(&p_sess->ftp_cmd_str, "XCUP"))
198 handle_cdup(p_sess);
200 else if (tunable_pasv_enable &&
201 !p_sess->epsv_all &&
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"))
227 handle_help(p_sess);
229 else if (tunable_dirlist_enable &&
230 str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
232 handle_list(p_sess);
234 else if (str_equal_text(&p_sess->ftp_cmd_str, "TYPE"))
236 handle_type(p_sess);
238 else if (tunable_port_enable &&
239 !p_sess->epsv_all &&
240 str_equal_text(&p_sess->ftp_cmd_str, "PORT"))
242 handle_port(p_sess);
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"))
248 handle_stor(p_sess);
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")))
255 handle_mkd(p_sess);
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")))
262 handle_rmd(p_sess);
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"))
268 handle_dele(p_sess);
270 else if (str_equal_text(&p_sess->ftp_cmd_str, "REST"))
272 handle_rest(p_sess);
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"))
278 handle_rnfr(p_sess);
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"))
284 handle_rnto(p_sess);
286 else if (tunable_dirlist_enable &&
287 str_equal_text(&p_sess->ftp_cmd_str, "NLST"))
289 handle_nlst(p_sess);
291 else if (str_equal_text(&p_sess->ftp_cmd_str, "SIZE"))
293 handle_size(p_sess);
295 else if (!p_sess->is_anonymous &&
296 str_equal_text(&p_sess->ftp_cmd_str, "SITE"))
298 handle_site(p_sess);
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"))
312 handle_appe(p_sess);
314 else if (str_equal_text(&p_sess->ftp_cmd_str, "MDTM"))
316 handle_mdtm(p_sess);
318 else if (tunable_port_enable &&
319 str_equal_text(&p_sess->ftp_cmd_str, "EPRT"))
321 handle_eprt(p_sess);
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.");
330 else
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.");
342 else
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"))
351 handle_stou(p_sess);
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"))
371 handle_feat(p_sess);
373 else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS"))
375 handle_opts(p_sess);
377 else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") &&
378 str_isempty(&p_sess->ftp_arg_str))
380 handle_stat(p_sess);
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"))
389 handle_pbsz(p_sess);
391 else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PROT"))
393 handle_prot(p_sess);
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. */
438 else
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);
449 static void
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);
464 static void
465 handle_cwd(struct vsf_session* p_sess)
467 int retval;
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.");
472 return;
474 retval = str_chdir(&p_sess->ftp_arg_str);
475 if (retval == 0)
477 /* Handle any messages */
478 vsf_banner_dir_changed(p_sess, FTP_CWDOK);
479 vsf_cmdio_write(p_sess, FTP_CWDOK, "Directory successfully changed.");
481 else
483 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to change directory.");
487 static void
488 handle_cdup(struct vsf_session* p_sess)
490 str_alloc_text(&p_sess->ftp_arg_str, "..");
491 handle_cwd(p_sess);
494 static int
495 port_active(struct vsf_session* p_sess)
497 int ret = 0;
498 if (p_sess->p_port_sockaddr != 0)
500 ret = 1;
501 if (pasv_active(p_sess))
503 bug("port and pasv both active");
506 return ret;
509 static int
510 pasv_active(struct vsf_session* p_sess)
512 int ret = 0;
513 if (tunable_one_process_model)
515 ret = vsf_one_process_pasv_active(p_sess);
517 else
519 ret = vsf_two_process_pasv_active(p_sess);
521 if (ret)
523 if (port_active(p_sess))
525 bug("pasv and port both active");
528 return ret;
531 static void
532 port_cleanup(struct vsf_session* p_sess)
534 vsf_sysutil_sockaddr_clear(&p_sess->p_port_sockaddr);
537 static void
538 pasv_cleanup(struct vsf_session* p_sess)
540 if (tunable_one_process_model)
542 vsf_one_process_pasv_cleanup(p_sess);
544 else
546 vsf_two_process_pasv_cleanup(p_sess);
550 static void
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))
559 int argval;
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.");
565 return;
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.");
571 return;
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);
580 else
582 the_port = vsf_two_process_listen(p_sess);
584 if (is_epsv)
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);
590 return;
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");
601 else
603 vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
605 str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
606 if (!is_ipv6)
608 str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
610 else
612 const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
613 if (p_v4addr)
615 str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
617 else
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);
631 static void
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;
637 int remote_fd;
638 int opened_file;
639 int is_ascii = 0;
640 filesize_t offset = p_sess->restart_pos;
641 p_sess->restart_pos = 0;
642 if (!is_http && !data_transfer_checks_ok(p_sess))
644 return;
646 if (p_sess->is_ascii && offset != 0)
648 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
649 "No support for resume of ASCII transfer.");
650 return;
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.");
659 return;
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.");
665 return;
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);
685 goto file_close_out;
687 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
688 * such as XFS DMAPI.
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.");
696 goto file_close_out;
698 /* Set the download offset (from REST) if any */
699 if (offset != 0)
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");
707 is_ascii = 1;
709 else
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).");
719 if (is_http)
721 remote_fd = VSFTP_COMMAND_FD;
723 else
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);
733 if (!is_http &&
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);
745 if (is_http)
747 goto file_close_out;
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.");
760 else
762 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
764 check_abor(p_sess);
765 port_pasv_cleanup_out:
766 port_cleanup(p_sess);
767 pasv_cleanup(p_sess);
768 file_close_out:
769 vsf_sysutil_close(opened_file);
772 static void
773 handle_list(struct vsf_session* p_sess)
775 handle_dir_common(p_sess, 1, 0);
778 static void
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;
787 int retval = 0;
788 int use_control = 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))
795 return;
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, ' ');
808 else
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.");
819 return;
821 /* First check - is it an outright directory, as in "ls /pub" */
822 p_dir = str_opendir(&s_filter_str);
823 if (p_dir != 0)
825 /* Listing a directory! */
826 str_copy(&s_dir_name_str, &s_filter_str);
827 str_free(&s_filter_str);
829 else
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, "/");
850 if (p_dir == 0)
852 /* NOTE - failure check done below, it's not forgotten */
853 p_dir = str_opendir(&s_dir_name_str);
855 /* Fine, do it */
856 if (stat_cmd)
858 use_control = 1;
859 str_append_char(&s_option_str, 'a');
860 vsf_cmdio_write_hyphen(p_sess, FTP_STATFILE_OK, "Status follows:");
862 else
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))
868 goto dir_close_out;
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))
876 dir_allow_read = 0;
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);
885 if (!stat_cmd)
887 if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && retval == 0)
889 retval = -1;
892 if (stat_cmd)
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.");
905 else
907 vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure writing network stream.");
909 check_abor(p_sess);
910 dir_close_out:
911 if (p_dir)
913 vsf_sysutil_closedir(p_dir);
915 if (!stat_cmd)
917 port_cleanup(p_sess);
918 pasv_cleanup(p_sess);
922 static void
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.");
939 else
941 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unrecognised TYPE command.");
945 static void
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,
954 sizeof(vals));
955 if (p_raw == 0)
957 vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
958 return;
960 the_port = vals[4] << 8;
961 the_port |= vals[5];
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);
965 /* SECURITY:
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);
977 return;
980 vsf_cmdio_write(p_sess, FTP_PORTOK,
981 "PORT command successful. Consider using PASV.");
984 static void
985 handle_stor(struct vsf_session* p_sess)
987 handle_upload_common(p_sess, 0, 0);
990 static void
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;
997 int new_file_fd;
998 int remote_fd;
999 int success = 0;
1000 int created = 0;
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))
1006 return;
1008 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
1009 p_filename = &p_sess->ftp_arg_str;
1010 if (is_unique)
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.");
1021 return;
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
1025 * upload?
1027 if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
1029 new_file_fd = str_create_exclusive(p_filename);
1031 else
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)
1037 do_truncate = 1;
1040 if (vsf_sysutil_retval_is_error(new_file_fd))
1042 vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
1043 return;
1045 created = 1;
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);
1062 else
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! */
1073 if (do_truncate)
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);
1083 else if (is_append)
1085 vsf_sysutil_lseek_end(new_file_fd);
1087 if (is_unique)
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);
1095 else
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,
1106 new_file_fd, 1, 1);
1108 else
1110 trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
1111 new_file_fd, 1, 0);
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)
1120 success = 1;
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.");
1131 else
1133 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
1135 check_abor(p_sess);
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);
1146 static void
1147 handle_mkd(struct vsf_session* p_sess)
1149 int retval;
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.");
1157 return;
1159 /* NOTE! Actual permissions will be governed by the tunable umask */
1160 retval = str_mkdir(&p_sess->ftp_arg_str, 0777);
1161 if (retval != 0)
1163 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1164 "Create directory operation failed.");
1165 return;
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);
1183 static void
1184 handle_rmd(struct vsf_session* p_sess)
1186 int retval;
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.");
1194 return;
1196 retval = str_rmdir(&p_sess->ftp_arg_str);
1197 if (retval != 0)
1199 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1200 "Remove directory operation failed.");
1202 else
1204 vsf_log_do_log(p_sess, 1);
1205 vsf_cmdio_write(p_sess, FTP_RMDIROK,
1206 "Remove directory operation successful.");
1210 static void
1211 handle_dele(struct vsf_session* p_sess)
1213 int retval;
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.");
1221 return;
1223 retval = str_unlink(&p_sess->ftp_arg_str);
1224 if (retval != 0)
1226 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
1228 else
1230 vsf_log_do_log(p_sess, 1);
1231 vsf_cmdio_write(p_sess, FTP_DELEOK, "Delete operation successful.");
1235 static void
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);
1240 if (val < 0)
1242 val = 0;
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);
1251 static void
1252 handle_rnfr(struct vsf_session* p_sess)
1254 static struct vsf_sysutil_statbuf* p_statbuf;
1255 int retval;
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.");
1265 return;
1267 /* Does it exist? */
1268 retval = str_stat(&p_sess->ftp_arg_str, &p_statbuf);
1269 if (retval == 0)
1271 /* Yes */
1272 str_copy(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
1273 vsf_cmdio_write(p_sess, FTP_RNFROK, "Ready for RNTO.");
1275 else
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.");
1284 static void
1285 handle_rnto(struct vsf_session* p_sess)
1287 static struct mystr s_tmp_str;
1288 int retval;
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.");
1294 return;
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.");
1307 return;
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);
1315 if (retval == 0)
1317 vsf_log_do_log(p_sess, 1);
1318 vsf_cmdio_write(p_sess, FTP_RENAMEOK, "Rename successful.");
1320 else
1322 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Rename failed.");
1326 static void
1327 handle_nlst(struct vsf_session* p_sess)
1329 handle_dir_common(p_sess, 0, 0);
1332 static void
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
1337 * relative
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);
1355 static void
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;
1361 unsigned int len;
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)
1366 return;
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
1372 * data.
1374 len = str_getlen(&async_cmd_str);
1375 if (len >= 4)
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
1384 * set to -1.
1386 vsf_sysutil_shutdown_failok(p_sess->data_fd);
1388 else
1390 /* Sorry! */
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);
1398 static int
1399 get_remote_transfer_fd(struct vsf_session* p_sess, const char* p_status_msg)
1401 int remote_fd;
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);
1411 else
1413 remote_fd = vsf_ftpdataio_get_port_fd(p_sess);
1415 if (vsf_sysutil_retval_is_error(remote_fd))
1417 return 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);
1423 return -1;
1425 return remote_fd;
1428 static void
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.");
1439 static void
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;
1448 int retval;
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.");
1453 return;
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.");
1460 else
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);
1469 static void
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");
1490 else
1492 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown SITE command.");
1496 static void
1497 handle_site_chmod(struct vsf_session* p_sess, struct mystr* p_arg_str)
1499 static struct mystr s_chmod_file_str;
1500 unsigned int perms;
1501 int retval;
1502 if (str_isempty(p_arg_str))
1504 vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
1505 return;
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.");
1511 return;
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.");
1522 return;
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.");
1531 else
1533 vsf_log_do_log(p_sess, 1);
1534 vsf_cmdio_write(p_sess, FTP_CHMODOK, "SITE CHMOD command ok.");
1538 static void
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()));
1549 else
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);
1561 static void
1562 handle_appe(struct vsf_session* p_sess)
1564 handle_upload_common(p_sess, 1, 0);
1567 static void
1568 handle_mdtm(struct vsf_session* p_sess)
1570 static struct mystr s_filename_str;
1571 static struct vsf_sysutil_statbuf* s_p_statbuf;
1572 int do_write = 0;
1573 long modtime = 0;
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) == '.'))
1582 do_write = 1;
1585 if (do_write != 0)
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.");
1595 return;
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.");
1606 else
1608 retval = vsf_sysutil_setmodtime(
1609 str_getbuf(&p_sess->ftp_arg_str), modtime, tunable_use_localtime);
1610 if (retval != 0)
1612 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1613 "Could not set file modification time.");
1615 else
1617 vsf_cmdio_write(p_sess, FTP_MDTMOK,
1618 "File modification time set.");
1622 else
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.");
1629 else
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);
1640 static void
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;
1646 int proto;
1647 int port;
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))
1656 goto bad_eprt;
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.");
1664 return;
1666 /* Split out address and parse it */
1667 str_split_char(&s_part1_str, &s_part2_str, '|');
1668 if (proto == 2)
1670 str_split_char(&s_part1_str, &s_scopeid_str, '%');
1671 p_raw_addr = vsf_sysutil_parse_ipv6(&s_part1_str);
1673 else
1675 p_raw_addr = vsf_sysutil_parse_ipv4(&s_part1_str);
1677 if (!p_raw_addr)
1679 goto bad_eprt;
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))
1685 goto bad_eprt;
1687 port = str_atoi(&s_part2_str);
1688 if (port < 0 || port > 65535)
1690 goto bad_eprt;
1692 vsf_sysutil_sockaddr_clone(&p_sess->p_port_sockaddr, p_sess->p_local_addr);
1693 if (proto == 2)
1695 vsf_sysutil_sockaddr_set_ipv6addr(p_sess->p_port_sockaddr, p_raw_addr);
1697 else
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);
1702 /* SECURITY:
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);
1714 return;
1717 vsf_cmdio_write(p_sess, FTP_EPRTOK,
1718 "EPRT command successful. Consider using EPSV.");
1719 return;
1720 bad_eprt:
1721 vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT command.");
1724 /* XXX - add AUTH etc. */
1725 static void
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,
1737 " XPWD XRMD\r\n");
1738 vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
1741 static void
1742 handle_stou(struct vsf_session* p_sess)
1744 handle_upload_common(p_sess, 0, 1);
1747 static void
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);
1760 return;
1762 while (1)
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))
1770 return;
1772 ++suffix;
1776 static void
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");
1791 else
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");
1799 else
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");
1809 else
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");
1820 else
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");
1828 else
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");
1843 static void
1844 handle_stat_file(struct vsf_session* p_sess)
1846 handle_dir_common(p_sess, 1, 1);
1849 static int
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.");
1855 return 0;
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)))
1861 vsf_cmdio_write(
1862 p_sess, FTP_NEEDENCRYPT, "Data connections must be encrypted.");
1863 return 0;
1865 return 1;
1868 static void
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);
1889 if (p_user != 0)
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.");
1912 else
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.");
1923 static void
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
1929 * logging works.
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");
1958 else
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);