vsftpd ver. 2.2.2 (11/17/2009)
[tomato.git] / release / src / router / vsftpd / postlogin.c
blobc2a3367383c97f013b981fcba758cd795d8611d9
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);
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,
69 int stat_cmd);
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,
76 int is_unique);
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);
82 void
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;
91 else
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.");
104 while(1)
106 int cmd_ok = 1;
107 if (tunable_setproctitle_enable)
109 vsf_sysutil_setproctitle("IDLE");
111 /* Blocks */
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);
134 while (1)
136 str_split_char(&s_src_str, &s_rhs_str, ',');
137 if (str_isempty(&s_src_str))
139 cmd_ok = 0;
140 break;
142 else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
144 break;
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);
154 while (1)
156 str_split_char(&s_src_str, &s_rhs_str, ',');
157 if (str_isempty(&s_src_str))
159 break;
161 else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
163 cmd_ok = 0;
164 break;
166 str_copy(&s_src_str, &s_rhs_str);
169 if (!cmd_ok)
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"))
180 handle_pwd(p_sess);
182 else if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") ||
183 str_equal_text(&p_sess->ftp_cmd_str, "XCWD"))
185 handle_cwd(p_sess);
187 else if (str_equal_text(&p_sess->ftp_cmd_str, "CDUP") ||
188 str_equal_text(&p_sess->ftp_cmd_str, "XCUP"))
190 handle_cdup(p_sess);
192 else if (tunable_pasv_enable &&
193 !p_sess->epsv_all &&
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"))
207 handle_retr(p_sess);
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"))
219 handle_help(p_sess);
221 else if (tunable_dirlist_enable &&
222 str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
224 handle_list(p_sess);
226 else if (str_equal_text(&p_sess->ftp_cmd_str, "TYPE"))
228 handle_type(p_sess);
230 else if (tunable_port_enable &&
231 !p_sess->epsv_all &&
232 str_equal_text(&p_sess->ftp_cmd_str, "PORT"))
234 handle_port(p_sess);
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"))
240 handle_stor(p_sess);
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")))
247 handle_mkd(p_sess);
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")))
254 handle_rmd(p_sess);
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"))
260 handle_dele(p_sess);
262 else if (str_equal_text(&p_sess->ftp_cmd_str, "REST"))
264 handle_rest(p_sess);
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"))
270 handle_rnfr(p_sess);
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"))
276 handle_rnto(p_sess);
278 else if (tunable_dirlist_enable &&
279 str_equal_text(&p_sess->ftp_cmd_str, "NLST"))
281 handle_nlst(p_sess);
283 else if (str_equal_text(&p_sess->ftp_cmd_str, "SIZE"))
285 handle_size(p_sess);
287 else if (!p_sess->is_anonymous &&
288 str_equal_text(&p_sess->ftp_cmd_str, "SITE"))
290 handle_site(p_sess);
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"))
304 handle_appe(p_sess);
306 else if (str_equal_text(&p_sess->ftp_cmd_str, "MDTM"))
308 handle_mdtm(p_sess);
310 else if (tunable_port_enable &&
311 str_equal_text(&p_sess->ftp_cmd_str, "EPRT"))
313 handle_eprt(p_sess);
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.");
322 else
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.");
334 else
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"))
343 handle_stou(p_sess);
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"))
363 handle_feat(p_sess);
365 else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS"))
367 handle_opts(p_sess);
369 else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") &&
370 str_isempty(&p_sess->ftp_arg_str))
372 handle_stat(p_sess);
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"))
381 handle_pbsz(p_sess);
383 else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PROT"))
385 handle_prot(p_sess);
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. */
430 else
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);
441 static void
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);
456 static void
457 handle_cwd(struct vsf_session* p_sess)
459 int retval;
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.");
464 return;
466 retval = str_chdir(&p_sess->ftp_arg_str);
467 if (retval == 0)
469 /* Handle any messages */
470 vsf_banner_dir_changed(p_sess, FTP_CWDOK);
471 vsf_cmdio_write(p_sess, FTP_CWDOK, "Directory successfully changed.");
473 else
475 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to change directory.");
479 static void
480 handle_cdup(struct vsf_session* p_sess)
482 str_alloc_text(&p_sess->ftp_arg_str, "..");
483 handle_cwd(p_sess);
486 static int
487 port_active(struct vsf_session* p_sess)
489 int ret = 0;
490 if (p_sess->p_port_sockaddr != 0)
492 ret = 1;
493 if (pasv_active(p_sess))
495 bug("port and pasv both active");
498 return ret;
501 static int
502 pasv_active(struct vsf_session* p_sess)
504 int ret = 0;
505 if (tunable_one_process_model)
507 ret = vsf_one_process_pasv_active(p_sess);
509 else
511 ret = vsf_two_process_pasv_active(p_sess);
513 if (ret)
515 if (port_active(p_sess))
517 bug("pasv and port both active");
520 return ret;
523 static void
524 port_cleanup(struct vsf_session* p_sess)
526 vsf_sysutil_sockaddr_clear(&p_sess->p_port_sockaddr);
529 static void
530 pasv_cleanup(struct vsf_session* p_sess)
532 if (tunable_one_process_model)
534 vsf_one_process_pasv_cleanup(p_sess);
536 else
538 vsf_two_process_pasv_cleanup(p_sess);
542 static void
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))
551 int argval;
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.");
557 return;
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.");
563 return;
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);
572 else
574 the_port = vsf_two_process_listen(p_sess);
576 if (is_epsv)
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);
582 return;
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");
593 else
595 vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
597 str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
598 if (!is_ipv6)
600 str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
602 else
604 const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
605 if (p_v4addr)
607 str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
609 else
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);
623 static void
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;
629 int remote_fd;
630 int opened_file;
631 int is_ascii = 0;
632 filesize_t offset = p_sess->restart_pos;
633 p_sess->restart_pos = 0;
634 if (!data_transfer_checks_ok(p_sess))
636 return;
638 if (p_sess->is_ascii && offset != 0)
640 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
641 "No support for resume of ASCII transfer.");
642 return;
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.");
651 return;
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.");
657 return;
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);
677 goto file_close_out;
679 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
680 * such as XFS DMAPI.
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.");
688 goto file_close_out;
690 /* Set the download offset (from REST) if any */
691 if (offset != 0)
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");
699 is_ascii = 1;
701 else
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.");
739 else
741 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
743 check_abor(p_sess);
744 port_pasv_cleanup_out:
745 port_cleanup(p_sess);
746 pasv_cleanup(p_sess);
747 file_close_out:
748 vsf_sysutil_close(opened_file);
751 static void
752 handle_list(struct vsf_session* p_sess)
754 handle_dir_common(p_sess, 1, 0);
757 static void
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;
766 int retval = 0;
767 int use_control = 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))
774 return;
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, ' ');
787 else
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.");
798 return;
800 /* First check - is it an outright directory, as in "ls /pub" */
801 p_dir = str_opendir(&s_filter_str);
802 if (p_dir != 0)
804 /* Listing a directory! */
805 str_copy(&s_dir_name_str, &s_filter_str);
806 str_free(&s_filter_str);
808 else
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, "/");
829 if (p_dir == 0)
831 /* NOTE - failure check done below, it's not forgotten */
832 p_dir = str_opendir(&s_dir_name_str);
834 /* Fine, do it */
835 if (stat_cmd)
837 use_control = 1;
838 str_append_char(&s_option_str, 'a');
839 vsf_cmdio_write_hyphen(p_sess, FTP_STATFILE_OK, "Status follows:");
841 else
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))
847 goto dir_close_out;
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))
855 dir_allow_read = 0;
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);
864 if (!stat_cmd)
866 if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && retval == 0)
868 retval = -1;
871 if (stat_cmd)
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.");
884 else
886 vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure writing network stream.");
888 check_abor(p_sess);
889 dir_close_out:
890 if (p_dir)
892 vsf_sysutil_closedir(p_dir);
894 if (!stat_cmd)
896 port_cleanup(p_sess);
897 pasv_cleanup(p_sess);
901 static void
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.");
918 else
920 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unrecognised TYPE command.");
924 static void
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,
933 sizeof(vals));
934 if (p_raw == 0)
936 vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
937 return;
939 the_port = vals[4] << 8;
940 the_port |= vals[5];
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);
944 /* SECURITY:
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);
956 return;
959 vsf_cmdio_write(p_sess, FTP_PORTOK,
960 "PORT command successful. Consider using PASV.");
963 static void
964 handle_stor(struct vsf_session* p_sess)
966 handle_upload_common(p_sess, 0, 0);
969 static void
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;
976 int new_file_fd;
977 int remote_fd;
978 int success = 0;
979 int created = 0;
980 int do_truncate = 0;
981 filesize_t offset = p_sess->restart_pos;
982 p_sess->restart_pos = 0;
983 if (!data_transfer_checks_ok(p_sess))
985 return;
987 resolve_tilde(&p_sess->ftp_arg_str, p_sess);
988 p_filename = &p_sess->ftp_arg_str;
989 if (is_unique)
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.");
1000 return;
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
1004 * upload?
1006 if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
1008 new_file_fd = str_create(p_filename);
1010 else
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)
1016 do_truncate = 1;
1019 if (vsf_sysutil_retval_is_error(new_file_fd))
1021 vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
1022 return;
1024 created = 1;
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);
1041 else
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! */
1052 if (do_truncate)
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
1062 * comes in.
1064 vsf_sysutil_lseek_to(new_file_fd, offset);
1066 if (is_unique)
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);
1074 else
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,
1085 new_file_fd, 1, 1);
1087 else
1089 trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
1090 new_file_fd, 1, 0);
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)
1099 success = 1;
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.");
1110 else
1112 vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer complete.");
1114 check_abor(p_sess);
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);
1125 static void
1126 handle_mkd(struct vsf_session* p_sess)
1128 int retval;
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.");
1136 return;
1138 /* NOTE! Actual permissions will be governed by the tunable umask */
1139 retval = str_mkdir(&p_sess->ftp_arg_str, 0777);
1140 if (retval != 0)
1142 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1143 "Create directory operation failed.");
1144 return;
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);
1162 static void
1163 handle_rmd(struct vsf_session* p_sess)
1165 int retval;
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.");
1173 return;
1175 retval = str_rmdir(&p_sess->ftp_arg_str);
1176 if (retval != 0)
1178 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1179 "Remove directory operation failed.");
1181 else
1183 vsf_log_do_log(p_sess, 1);
1184 vsf_cmdio_write(p_sess, FTP_RMDIROK,
1185 "Remove directory operation successful.");
1189 static void
1190 handle_dele(struct vsf_session* p_sess)
1192 int retval;
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.");
1200 return;
1202 retval = str_unlink(&p_sess->ftp_arg_str);
1203 if (retval != 0)
1205 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
1207 else
1209 vsf_log_do_log(p_sess, 1);
1210 vsf_cmdio_write(p_sess, FTP_DELEOK, "Delete operation successful.");
1214 static void
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);
1219 if (val < 0)
1221 val = 0;
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);
1230 static void
1231 handle_rnfr(struct vsf_session* p_sess)
1233 static struct vsf_sysutil_statbuf* p_statbuf;
1234 int retval;
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.");
1244 return;
1246 /* Does it exist? */
1247 retval = str_stat(&p_sess->ftp_arg_str, &p_statbuf);
1248 if (retval == 0)
1250 /* Yes */
1251 str_copy(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
1252 vsf_cmdio_write(p_sess, FTP_RNFROK, "Ready for RNTO.");
1254 else
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.");
1263 static void
1264 handle_rnto(struct vsf_session* p_sess)
1266 static struct mystr s_tmp_str;
1267 int retval;
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.");
1273 return;
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.");
1286 return;
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);
1294 if (retval == 0)
1296 vsf_log_do_log(p_sess, 1);
1297 vsf_cmdio_write(p_sess, FTP_RENAMEOK, "Rename successful.");
1299 else
1301 vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Rename failed.");
1305 static void
1306 handle_nlst(struct vsf_session* p_sess)
1308 handle_dir_common(p_sess, 0, 0);
1311 static void
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
1316 * relative
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);
1334 static void
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;
1340 unsigned int len;
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)
1345 return;
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
1351 * data.
1353 len = str_getlen(&async_cmd_str);
1354 if (len >= 4)
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
1363 * set to -1.
1365 vsf_sysutil_shutdown_failok(p_sess->data_fd);
1367 else
1369 /* Sorry! */
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);
1377 static int
1378 get_remote_transfer_fd(struct vsf_session* p_sess, const char* p_status_msg)
1380 int remote_fd;
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);
1390 else
1392 remote_fd = vsf_ftpdataio_get_port_fd(p_sess);
1394 if (vsf_sysutil_retval_is_error(remote_fd))
1396 return 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);
1402 return -1;
1404 return remote_fd;
1407 static void
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.");
1418 static void
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;
1427 int retval;
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.");
1432 return;
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.");
1439 else
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);
1448 static void
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");
1469 else
1471 vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown SITE command.");
1475 static void
1476 handle_site_chmod(struct vsf_session* p_sess, struct mystr* p_arg_str)
1478 static struct mystr s_chmod_file_str;
1479 unsigned int perms;
1480 int retval;
1481 if (str_isempty(p_arg_str))
1483 vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
1484 return;
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.");
1490 return;
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.");
1501 return;
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.");
1510 else
1512 vsf_log_do_log(p_sess, 1);
1513 vsf_cmdio_write(p_sess, FTP_CHMODOK, "SITE CHMOD command ok.");
1517 static void
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()));
1528 else
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);
1540 static void
1541 handle_appe(struct vsf_session* p_sess)
1543 handle_upload_common(p_sess, 1, 0);
1546 static void
1547 handle_mdtm(struct vsf_session* p_sess)
1549 static struct mystr s_filename_str;
1550 static struct vsf_sysutil_statbuf* s_p_statbuf;
1551 int do_write = 0;
1552 long modtime = 0;
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) == '.'))
1561 do_write = 1;
1564 if (do_write != 0)
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.");
1574 return;
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.");
1585 else
1587 retval = vsf_sysutil_setmodtime(
1588 str_getbuf(&p_sess->ftp_arg_str), modtime, tunable_use_localtime);
1589 if (retval != 0)
1591 vsf_cmdio_write(p_sess, FTP_FILEFAIL,
1592 "Could not set file modification time.");
1594 else
1596 vsf_cmdio_write(p_sess, FTP_MDTMOK,
1597 "File modification time set.");
1601 else
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.");
1608 else
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);
1619 static void
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;
1625 int proto;
1626 int port;
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))
1635 goto bad_eprt;
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.");
1643 return;
1645 /* Split out address and parse it */
1646 str_split_char(&s_part1_str, &s_part2_str, '|');
1647 if (proto == 2)
1649 str_split_char(&s_part1_str, &s_scopeid_str, '%');
1650 p_raw_addr = vsf_sysutil_parse_ipv6(&s_part1_str);
1652 else
1654 p_raw_addr = vsf_sysutil_parse_ipv4(&s_part1_str);
1656 if (!p_raw_addr)
1658 goto bad_eprt;
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))
1664 goto bad_eprt;
1666 port = str_atoi(&s_part2_str);
1667 if (port < 0 || port > 65535)
1669 goto bad_eprt;
1671 vsf_sysutil_sockaddr_clone(&p_sess->p_port_sockaddr, p_sess->p_local_addr);
1672 if (proto == 2)
1674 vsf_sysutil_sockaddr_set_ipv6addr(p_sess->p_port_sockaddr, p_raw_addr);
1676 else
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);
1681 /* SECURITY:
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);
1693 return;
1696 vsf_cmdio_write(p_sess, FTP_EPRTOK,
1697 "EPRT command successful. Consider using EPSV.");
1698 return;
1699 bad_eprt:
1700 vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT command.");
1703 /* XXX - add AUTH etc. */
1704 static void
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,
1716 " XPWD XRMD\r\n");
1717 vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
1720 static void
1721 handle_stou(struct vsf_session* p_sess)
1723 handle_upload_common(p_sess, 0, 1);
1726 static void
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);
1739 return;
1741 while (1)
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))
1749 return;
1751 ++suffix;
1755 static void
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");
1770 else
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");
1778 else
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");
1788 else
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");
1799 else
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");
1807 else
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");
1822 static void
1823 handle_stat_file(struct vsf_session* p_sess)
1825 handle_dir_common(p_sess, 1, 1);
1828 static int
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.");
1834 return 0;
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)))
1840 vsf_cmdio_write(
1841 p_sess, FTP_NEEDENCRYPT, "Data connections must be encrypted.");
1842 return 0;
1844 return 1;
1847 static void
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);
1868 if (p_user != 0)
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.");
1891 else
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.");