diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-16 16:19:53 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-16 16:19:53 +0000 |
commit | 20c82168976a511237b45eef94891e9124f47f7a (patch) | |
tree | 034de657c181a5dbe623b9fc670cec637231b257 /networking/ftpd.c | |
parent | f2160b6a09f4e4879fb718526106c548d8f8ec23 (diff) | |
download | busybox-w32-20c82168976a511237b45eef94891e9124f47f7a.tar.gz busybox-w32-20c82168976a511237b45eef94891e9124f47f7a.tar.bz2 busybox-w32-20c82168976a511237b45eef94891e9124f47f7a.zip |
ftpd: add idle and absolute timeouts. This is a security issue,
otherwise ftpd may end up hanging indefinitely.
function old new delta
timeout_handler - 110 +110
ftpd_main 2019 2115 +96
packed_usage 25662 25685 +23
handle_upload_common 306 322 +16
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 245/0) Total: 245 bytes
Diffstat (limited to 'networking/ftpd.c')
-rw-r--r-- | networking/ftpd.c | 70 |
1 files changed, 50 insertions, 20 deletions
diff --git a/networking/ftpd.c b/networking/ftpd.c index 22cec83a8..d63fd9bed 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c | |||
@@ -8,11 +8,6 @@ | |||
8 | * | 8 | * |
9 | * Only subset of FTP protocol is implemented but vast majority of clients | 9 | * Only subset of FTP protocol is implemented but vast majority of clients |
10 | * should not have any problem. You have to run this daemon via inetd. | 10 | * should not have any problem. You have to run this daemon via inetd. |
11 | * | ||
12 | * Options: | ||
13 | * -w - enable FTP write commands | ||
14 | * | ||
15 | * TODO: implement "421 Timeout" thingy (alarm(60) while waiting for a cmd). | ||
16 | */ | 11 | */ |
17 | 12 | ||
18 | #include "libbb.h" | 13 | #include "libbb.h" |
@@ -46,6 +41,7 @@ | |||
46 | #define FTP_GIVEPWORD 331 | 41 | #define FTP_GIVEPWORD 331 |
47 | #define FTP_RESTOK 350 | 42 | #define FTP_RESTOK 350 |
48 | #define FTP_RNFROK 350 | 43 | #define FTP_RNFROK 350 |
44 | #define FTP_TIMEOUT 421 | ||
49 | #define FTP_BADSENDCONN 425 | 45 | #define FTP_BADSENDCONN 425 |
50 | #define FTP_BADSENDNET 426 | 46 | #define FTP_BADSENDNET 426 |
51 | #define FTP_BADSENDFILE 451 | 47 | #define FTP_BADSENDFILE 451 |
@@ -77,12 +73,16 @@ enum { | |||
77 | ) | 73 | ) |
78 | 74 | ||
79 | struct globals { | 75 | struct globals { |
80 | char *p_control_line_buf; | ||
81 | len_and_sockaddr *local_addr; | ||
82 | len_and_sockaddr *port_addr; | ||
83 | int pasv_listen_fd; | 76 | int pasv_listen_fd; |
84 | int proc_self_fd; | 77 | int proc_self_fd; |
78 | int local_file_fd; | ||
79 | int start_time; | ||
80 | int abs_timeout; | ||
81 | int timeout; | ||
82 | off_t local_file_pos; | ||
85 | off_t restart_pos; | 83 | off_t restart_pos; |
84 | len_and_sockaddr *local_addr; | ||
85 | len_and_sockaddr *port_addr; | ||
86 | char *ftp_cmd; | 86 | char *ftp_cmd; |
87 | char *ftp_arg; | 87 | char *ftp_arg; |
88 | #if ENABLE_FEATURE_FTP_WRITE | 88 | #if ENABLE_FEATURE_FTP_WRITE |
@@ -179,6 +179,33 @@ cmdio_write_raw(const char *p_text) | |||
179 | xwrite_str(STDOUT_FILENO, p_text); | 179 | xwrite_str(STDOUT_FILENO, p_text); |
180 | } | 180 | } |
181 | 181 | ||
182 | static void | ||
183 | timeout_handler(int sig UNUSED_PARAM) | ||
184 | { | ||
185 | off_t pos; | ||
186 | int sv_errno = errno; | ||
187 | |||
188 | if (monotonic_sec() - G.start_time > G.abs_timeout) | ||
189 | goto timed_out; | ||
190 | |||
191 | if (!G.local_file_fd) | ||
192 | goto timed_out; | ||
193 | |||
194 | pos = xlseek(G.local_file_fd, 0, SEEK_CUR); | ||
195 | if (pos == G.local_file_pos) | ||
196 | goto timed_out; | ||
197 | G.local_file_pos = pos; | ||
198 | |||
199 | alarm(G.timeout); | ||
200 | errno = sv_errno; | ||
201 | return; | ||
202 | |||
203 | timed_out: | ||
204 | cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n"); | ||
205 | /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */ | ||
206 | exit(1); | ||
207 | } | ||
208 | |||
182 | /* Simple commands */ | 209 | /* Simple commands */ |
183 | 210 | ||
184 | static void | 211 | static void |
@@ -488,6 +515,7 @@ handle_retr(void) | |||
488 | cmdio_write_error(FTP_FILEFAIL); | 515 | cmdio_write_error(FTP_FILEFAIL); |
489 | goto file_close_out; | 516 | goto file_close_out; |
490 | } | 517 | } |
518 | G.local_file_fd = local_file_fd; | ||
491 | 519 | ||
492 | /* Now deactive O_NONBLOCK, otherwise we have a problem | 520 | /* Now deactive O_NONBLOCK, otherwise we have a problem |
493 | * on DMAPI filesystems such as XFS DMAPI. | 521 | * on DMAPI filesystems such as XFS DMAPI. |
@@ -506,11 +534,6 @@ handle_retr(void) | |||
506 | if (remote_fd < 0) | 534 | if (remote_fd < 0) |
507 | goto file_close_out; | 535 | goto file_close_out; |
508 | 536 | ||
509 | /* TODO: if we'll implement timeout, this will need more clever handling. | ||
510 | * Perhaps alarm(N) + checking that current position on local_file_fd | ||
511 | * is advancing. As of now, peer may stall us indefinitely. | ||
512 | */ | ||
513 | |||
514 | bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd); | 537 | bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd); |
515 | close(remote_fd); | 538 | close(remote_fd); |
516 | if (bytes_transferred < 0) | 539 | if (bytes_transferred < 0) |
@@ -520,6 +543,7 @@ handle_retr(void) | |||
520 | 543 | ||
521 | file_close_out: | 544 | file_close_out: |
522 | close(local_file_fd); | 545 | close(local_file_fd); |
546 | G.local_file_fd = 0; | ||
523 | } | 547 | } |
524 | 548 | ||
525 | /* List commands */ | 549 | /* List commands */ |
@@ -753,6 +777,7 @@ handle_upload_common(int is_append, int is_unique) | |||
753 | goto close_local_and_bail; | 777 | goto close_local_and_bail; |
754 | return; | 778 | return; |
755 | } | 779 | } |
780 | G.local_file_fd = local_file_fd; | ||
756 | 781 | ||
757 | if (offset) | 782 | if (offset) |
758 | xlseek(local_file_fd, offset, SEEK_SET); | 783 | xlseek(local_file_fd, offset, SEEK_SET); |
@@ -763,11 +788,6 @@ handle_upload_common(int is_append, int is_unique) | |||
763 | if (remote_fd < 0) | 788 | if (remote_fd < 0) |
764 | goto close_local_and_bail; | 789 | goto close_local_and_bail; |
765 | 790 | ||
766 | /* TODO: if we'll implement timeout, this will need more clever handling. | ||
767 | * Perhaps alarm(N) + checking that current position on local_file_fd | ||
768 | * is advancing. As of now, peer may stall us indefinitely. | ||
769 | */ | ||
770 | |||
771 | bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd); | 791 | bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd); |
772 | close(remote_fd); | 792 | close(remote_fd); |
773 | if (bytes_transferred < 0) | 793 | if (bytes_transferred < 0) |
@@ -777,6 +797,7 @@ handle_upload_common(int is_append, int is_unique) | |||
777 | 797 | ||
778 | close_local_and_bail: | 798 | close_local_and_bail: |
779 | close(local_file_fd); | 799 | close(local_file_fd); |
800 | G.local_file_fd = 0; | ||
780 | } | 801 | } |
781 | 802 | ||
782 | static void | 803 | static void |
@@ -807,6 +828,8 @@ cmdio_get_cmd_and_arg(void) | |||
807 | uint32_t cmdval; | 828 | uint32_t cmdval; |
808 | char *cmd; | 829 | char *cmd; |
809 | 830 | ||
831 | alarm(G.timeout); | ||
832 | |||
810 | free(G.ftp_cmd); | 833 | free(G.ftp_cmd); |
811 | len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */ | 834 | len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */ |
812 | G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len); | 835 | G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len); |
@@ -878,17 +901,23 @@ int ftpd_main(int argc, char **argv) | |||
878 | { | 901 | { |
879 | smallint opts; | 902 | smallint opts; |
880 | 903 | ||
881 | opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w")); | 904 | INIT_G(); |
905 | |||
906 | G.start_time = monotonic_sec(); | ||
907 | G.abs_timeout = 1 * 60 * 60; | ||
908 | G.timeout = 2 * 60; | ||
909 | opt_complementary = "t+:T+"; | ||
910 | opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &G.abs_timeout); | ||
882 | 911 | ||
883 | if (opts & (OPT_l|OPT_1)) { | 912 | if (opts & (OPT_l|OPT_1)) { |
884 | /* Our secret backdoor to ls */ | 913 | /* Our secret backdoor to ls */ |
914 | memset(&G, 0, sizeof(G)); | ||
885 | /* TODO: pass -n too? */ | 915 | /* TODO: pass -n too? */ |
886 | xchdir(argv[2]); | 916 | xchdir(argv[2]); |
887 | argv[2] = (char*)"--"; | 917 | argv[2] = (char*)"--"; |
888 | return ls_main(argc, argv); | 918 | return ls_main(argc, argv); |
889 | } | 919 | } |
890 | 920 | ||
891 | INIT_G(); | ||
892 | 921 | ||
893 | G.local_addr = get_sock_lsa(STDIN_FILENO); | 922 | G.local_addr = get_sock_lsa(STDIN_FILENO); |
894 | if (!G.local_addr) { | 923 | if (!G.local_addr) { |
@@ -927,6 +956,7 @@ int ftpd_main(int argc, char **argv) | |||
927 | setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1)); | 956 | setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1)); |
928 | 957 | ||
929 | cmdio_write_ok(FTP_GREET); | 958 | cmdio_write_ok(FTP_GREET); |
959 | signal(SIGALRM, timeout_handler); | ||
930 | 960 | ||
931 | #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN | 961 | #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN |
932 | { | 962 | { |