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 | |
| 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
| -rw-r--r-- | include/usage.h | 3 | ||||
| -rw-r--r-- | networking/ftpd.c | 70 |
2 files changed, 52 insertions, 21 deletions
diff --git a/include/usage.h b/include/usage.h index 25a716278..e78754315 100644 --- a/include/usage.h +++ b/include/usage.h | |||
| @@ -1281,7 +1281,7 @@ | |||
| 1281 | "\n -f Force file system check" \ | 1281 | "\n -f Force file system check" \ |
| 1282 | 1282 | ||
| 1283 | #define ftpd_trivial_usage \ | 1283 | #define ftpd_trivial_usage \ |
| 1284 | "[-wvS] [DIR]" | 1284 | "[-wvS] [-t N] [-T N] [DIR]" |
| 1285 | #define ftpd_full_usage "\n\n" \ | 1285 | #define ftpd_full_usage "\n\n" \ |
| 1286 | "FTP server\n" \ | 1286 | "FTP server\n" \ |
| 1287 | "\n" \ | 1287 | "\n" \ |
| @@ -1294,6 +1294,7 @@ | |||
| 1294 | "\n -w Allow upload" \ | 1294 | "\n -w Allow upload" \ |
| 1295 | "\n -v Log to stderr" \ | 1295 | "\n -v Log to stderr" \ |
| 1296 | "\n -S Log to syslog" \ | 1296 | "\n -S Log to syslog" \ |
| 1297 | "\n -t,-T Idle and absolute timeouts" \ | ||
| 1297 | "\n DIR Change root to this directory" \ | 1298 | "\n DIR Change root to this directory" \ |
| 1298 | 1299 | ||
| 1299 | #define ftpget_trivial_usage \ | 1300 | #define ftpget_trivial_usage \ |
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 | { |
