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 | |
parent | f2160b6a09f4e4879fb718526106c548d8f8ec23 (diff) |
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')
-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 @@ * * Only subset of FTP protocol is implemented but vast majority of clients * should not have any problem. You have to run this daemon via inetd. - * - * Options: - * -w - enable FTP write commands - * - * TODO: implement "421 Timeout" thingy (alarm(60) while waiting for a cmd). */ #include "libbb.h" @@ -46,6 +41,7 @@ #define FTP_GIVEPWORD 331 #define FTP_RESTOK 350 #define FTP_RNFROK 350 +#define FTP_TIMEOUT 421 #define FTP_BADSENDCONN 425 #define FTP_BADSENDNET 426 #define FTP_BADSENDFILE 451 @@ -77,12 +73,16 @@ enum { ) struct globals { - char *p_control_line_buf; - len_and_sockaddr *local_addr; - len_and_sockaddr *port_addr; int pasv_listen_fd; int proc_self_fd; + int local_file_fd; + int start_time; + int abs_timeout; + int timeout; + off_t local_file_pos; off_t restart_pos; + len_and_sockaddr *local_addr; + len_and_sockaddr *port_addr; char *ftp_cmd; char *ftp_arg; #if ENABLE_FEATURE_FTP_WRITE @@ -179,6 +179,33 @@ cmdio_write_raw(const char *p_text) xwrite_str(STDOUT_FILENO, p_text); } +static void +timeout_handler(int sig UNUSED_PARAM) +{ + off_t pos; + int sv_errno = errno; + + if (monotonic_sec() - G.start_time > G.abs_timeout) + goto timed_out; + + if (!G.local_file_fd) + goto timed_out; + + pos = xlseek(G.local_file_fd, 0, SEEK_CUR); + if (pos == G.local_file_pos) + goto timed_out; + G.local_file_pos = pos; + + alarm(G.timeout); + errno = sv_errno; + return; + + timed_out: + cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n"); +/* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */ + exit(1); +} + /* Simple commands */ static void @@ -488,6 +515,7 @@ handle_retr(void) cmdio_write_error(FTP_FILEFAIL); goto file_close_out; } + G.local_file_fd = local_file_fd; /* Now deactive O_NONBLOCK, otherwise we have a problem * on DMAPI filesystems such as XFS DMAPI. @@ -506,11 +534,6 @@ handle_retr(void) if (remote_fd < 0) goto file_close_out; -/* TODO: if we'll implement timeout, this will need more clever handling. - * Perhaps alarm(N) + checking that current position on local_file_fd - * is advancing. As of now, peer may stall us indefinitely. - */ - bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd); close(remote_fd); if (bytes_transferred < 0) @@ -520,6 +543,7 @@ handle_retr(void) file_close_out: close(local_file_fd); + G.local_file_fd = 0; } /* List commands */ @@ -753,6 +777,7 @@ handle_upload_common(int is_append, int is_unique) goto close_local_and_bail; return; } + G.local_file_fd = local_file_fd; if (offset) xlseek(local_file_fd, offset, SEEK_SET); @@ -763,11 +788,6 @@ handle_upload_common(int is_append, int is_unique) if (remote_fd < 0) goto close_local_and_bail; -/* TODO: if we'll implement timeout, this will need more clever handling. - * Perhaps alarm(N) + checking that current position on local_file_fd - * is advancing. As of now, peer may stall us indefinitely. - */ - bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd); close(remote_fd); if (bytes_transferred < 0) @@ -777,6 +797,7 @@ handle_upload_common(int is_append, int is_unique) close_local_and_bail: close(local_file_fd); + G.local_file_fd = 0; } static void @@ -807,6 +828,8 @@ cmdio_get_cmd_and_arg(void) uint32_t cmdval; char *cmd; + alarm(G.timeout); + free(G.ftp_cmd); len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */ G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len); @@ -878,17 +901,23 @@ int ftpd_main(int argc, char **argv) { smallint opts; - opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w")); + INIT_G(); + + G.start_time = monotonic_sec(); + G.abs_timeout = 1 * 60 * 60; + G.timeout = 2 * 60; + opt_complementary = "t+:T+"; + opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &G.abs_timeout); if (opts & (OPT_l|OPT_1)) { /* Our secret backdoor to ls */ + memset(&G, 0, sizeof(G)); /* TODO: pass -n too? */ xchdir(argv[2]); argv[2] = (char*)"--"; return ls_main(argc, argv); } - INIT_G(); G.local_addr = get_sock_lsa(STDIN_FILENO); if (!G.local_addr) { @@ -927,6 +956,7 @@ int ftpd_main(int argc, char **argv) setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1)); cmdio_write_ok(FTP_GREET); + signal(SIGALRM, timeout_handler); #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN { |