X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Fftpd.c;h=6ca231c90b203d4ee54a6fb7fe46c5188b61441b;hb=42a8984abc5eed709addac263aab43d64d47257d;hp=6adcb1dee0cfcfdfba4890666c483e899e6917e2;hpb=7666fa1d02b4e7e74a83833881caef09b475b88b;p=oweals%2Fbusybox.git diff --git a/networking/ftpd.c b/networking/ftpd.c index 6adcb1dee..6ca231c90 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c @@ -11,24 +11,78 @@ * * You have to run this daemon via inetd. */ +//config:config FTPD +//config: bool "ftpd (30 kb)" +//config: default y +//config: help +//config: Simple FTP daemon. You have to run it via inetd. +//config: +//config:config FEATURE_FTPD_WRITE +//config: bool "Enable -w (upload commands)" +//config: default y +//config: depends on FTPD +//config: help +//config: Enable -w option. "ftpd -w" will accept upload commands +//config: such as STOR, STOU, APPE, DELE, MKD, RMD, rename commands. +//config: +//config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST +//config: bool "Enable workaround for RFC-violating clients" +//config: default y +//config: depends on FTPD +//config: help +//config: Some ftp clients (among them KDE's Konqueror) issue illegal +//config: "LIST -l" requests. This option works around such problems. +//config: It might prevent you from listing files starting with "-" and +//config: it increases the code size by ~40 bytes. +//config: Most other ftp servers seem to behave similar to this. +//config: +//config:config FEATURE_FTPD_AUTHENTICATION +//config: bool "Enable authentication" +//config: default y +//config: depends on FTPD +//config: help +//config: Require login, and change to logged in user's UID:GID before +//config: accessing any files. Option "-a USER" allows "anonymous" +//config: logins (treats them as if USER logged in). +//config: +//config: If this option is not selected, ftpd runs with the rights +//config: of the user it was started under, and does not require login. +//config: Take care to not launch it under root. + +//applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_FTPD) += ftpd.o //usage:#define ftpd_trivial_usage -//usage: "[-wvS] [-t N] [-T N] [DIR]" +//usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t N] [-T N] [DIR]" //usage:#define ftpd_full_usage "\n\n" -//usage: "Anonymous FTP server\n" -//usage: "\n" -//usage: "ftpd should be used as an inetd service.\n" -//usage: "ftpd's line for inetd.conf:\n" +//usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION( +//usage: "Anonymous FTP server. Client access occurs under ftpd's UID.\n" +//usage: ) +//usage: IF_FEATURE_FTPD_AUTHENTICATION( +//usage: "FTP server. " +//usage: ) +//usage: "Chroots to DIR, if this fails (run by non-root), cds to it.\n" +//usage: "Should be used as inetd service, inetd.conf line:\n" //usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n" -//usage: "It also can be ran from tcpsvd:\n" -//usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve\n" +//usage: "Can be run from tcpsvd:\n" +//usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve" +//usage: "\n" //usage: "\n -w Allow upload" +//usage: IF_FEATURE_FTPD_AUTHENTICATION( +//usage: "\n -A No login required, client access occurs under ftpd's UID" +// +// if !FTPD_AUTHENTICATION, -A is accepted too, but not shown in --help +// since it's the only supported mode in that configuration +// +//usage: "\n -a USER Enable 'anonymous' login and map it to USER" +//usage: ) //usage: "\n -v Log errors to stderr. -vv: verbose log" //usage: "\n -S Log errors to syslog. -SS: verbose log" -//usage: "\n -t,-T Idle and absolute timeouts" -//usage: "\n DIR Change root to this directory" +//usage: "\n -t,-T N Idle and absolute timeout" #include "libbb.h" +#include "common_bufsiz.h" #include #include @@ -116,15 +170,20 @@ struct globals { len_and_sockaddr *port_addr; char *ftp_cmd; char *ftp_arg; -#if ENABLE_FEATURE_FTP_WRITE +#if ENABLE_FEATURE_FTPD_WRITE char *rnfr_filename; #endif /* We need these aligned to uint32_t */ char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc]; char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc]; } FIX_ALIASING; -#define G (*(struct globals*)&bb_common_bufsiz1) +#define G (*ptr_to_globals) +/* ^^^ about 75 bytes smaller code than this: */ +//#define G (*(struct globals*)bb_common_bufsiz1) #define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + /*setup_common_bufsiz();*/ \ + \ /* Moved to main */ \ /*strcpy(G.msg_ok + 4, MSG_OK );*/ \ /*strcpy(G.msg_err + 4, MSG_ERR);*/ \ @@ -209,7 +268,7 @@ cmdio_write(uint32_t status_str, const char *str) static void cmdio_write_ok(unsigned status) { - *(uint32_t *) G.msg_ok = status; + *(bb__aliased_uint32_t *) G.msg_ok = status; xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1); if (G.verbose > 1) verbose_log(G.msg_ok); @@ -220,7 +279,7 @@ cmdio_write_ok(unsigned status) static void cmdio_write_error(unsigned status) { - *(uint32_t *) G.msg_err = status; + *(bb__aliased_uint32_t *) G.msg_err = status; xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1); if (G.verbose > 0) verbose_log(G.msg_err); @@ -377,7 +436,7 @@ ftpdataio_get_pasv_fd(void) return remote_fd; } - setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); + setsockopt_keepalive(remote_fd); return remote_fd; } @@ -550,7 +609,7 @@ static void handle_rest(void) { /* When ftp_arg == NULL simply restart from beginning */ - G.restart_pos = G.ftp_arg ? xatoi_positive(G.ftp_arg) : 0; + G.restart_pos = G.ftp_arg ? XATOOFF(G.ftp_arg) : 0; WRITE_OK(FTP_RESTOK); } @@ -659,7 +718,7 @@ popen_ls(const char *opt) dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */ #if BB_MMU /* memset(&G, 0, sizeof(G)); - ls_main does it */ - exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv)); + exit(ls_main(/*argc_unused*/ 0, (char**) argv)); #else cur_fd = xopen(".", O_RDONLY | O_DIRECTORY); /* On NOMMU, we want to execute a child - copy of ourself @@ -829,7 +888,7 @@ handle_size_or_mdtm(int need_size) /* Upload commands */ -#if ENABLE_FEATURE_FTP_WRITE +#if ENABLE_FEATURE_FTPD_WRITE static void handle_mkd(void) { @@ -972,7 +1031,7 @@ handle_stou(void) G.restart_pos = 0; handle_upload_common(0, 1); } -#endif /* ENABLE_FEATURE_FTP_WRITE */ +#endif /* ENABLE_FEATURE_FTPD_WRITE */ static uint32_t cmdio_get_cmd_and_arg(void) @@ -1102,20 +1161,21 @@ enum { #if !BB_MMU OPT_l = (1 << 0), OPT_1 = (1 << 1), - OPT_A = (1 << 2), #endif - OPT_v = (1 << ((!BB_MMU) * 3 + 0)), - OPT_S = (1 << ((!BB_MMU) * 3 + 1)), - OPT_w = (1 << ((!BB_MMU) * 3 + 2)) * ENABLE_FEATURE_FTP_WRITE, + BIT_A = (!BB_MMU) * 2, + OPT_A = (1 << (BIT_A + 0)), + OPT_v = (1 << (BIT_A + 1)), + OPT_S = (1 << (BIT_A + 2)), + OPT_w = (1 << (BIT_A + 3)) * ENABLE_FEATURE_FTPD_WRITE, }; int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -#if !BB_MMU -int ftpd_main(int argc, char **argv) -#else int ftpd_main(int argc UNUSED_PARAM, char **argv) -#endif { +#if ENABLE_FEATURE_FTPD_AUTHENTICATION + struct passwd *pw = NULL; + char *anon_opt = NULL; +#endif unsigned abs_timeout; unsigned verbose_S; smallint opts; @@ -1125,18 +1185,27 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) abs_timeout = 1 * 60 * 60; verbose_S = 0; G.timeout = 2 * 60; - opt_complementary = "t+:T+:vv:SS"; #if BB_MMU - opts = getopt32(argv, "vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S); + opts = getopt32(argv, "^" "AvS" IF_FEATURE_FTPD_WRITE("w") + "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:") + "\0" "vv:SS", + &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,) + &G.verbose, &verbose_S + ); #else - opts = getopt32(argv, "l1AvS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S); + opts = getopt32(argv, "^" "l1AvS" IF_FEATURE_FTPD_WRITE("w") + "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:") + "\0" "vv:SS", + &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,) + &G.verbose, &verbose_S + ); if (opts & (OPT_l|OPT_1)) { - /* Our secret backdoor to ls */ -/* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */ + /* Our secret backdoor to ls: see popen_ls() */ if (fchdir(3) != 0) _exit(127); /* memset(&G, 0, sizeof(G)); - ls_main does it */ - return ls_main(argc, argv); + /* NB: in this case -A has a different meaning: like "ls -A" */ + return ls_main(/*argc_unused*/ 0, argv); } #endif if (G.verbose < verbose_S) @@ -1174,29 +1243,36 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) //umask(077); - admin can set umask before starting us - /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */ - signal(SIGPIPE, SIG_IGN); + /* Signals */ + bb_signals(0 + /* We'll always take EPIPE rather than a rude signal, thanks */ + + (1 << SIGPIPE) + /* LIST command spawns chilren. Prevent zombies */ + + (1 << SIGCHLD) + , SIG_IGN); /* Set up options on the command socket (do we need these all? why?) */ - setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1)); - setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); + setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY); + setsockopt_keepalive(STDIN_FILENO); /* Telnet protocol over command link may send "urgent" data, * we prefer it to be received in the "normal" data stream: */ - setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1)); + setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE); WRITE_OK(FTP_GREET); signal(SIGALRM, timeout_handler); -#if ENABLE_FEATURE_FTP_AUTHENTICATION - { - struct passwd *pw = NULL; - +#if ENABLE_FEATURE_FTPD_AUTHENTICATION + if (!(opts & OPT_A)) { while (1) { uint32_t cmdval = cmdio_get_cmd_and_arg(); - if (cmdval == const_USER) { + if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) { + pw = getpwnam(anon_opt); + if (pw) + break; /* does not even ask for password */ + } pw = getpwnam(G.ftp_arg); - cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n"); + cmdio_write_raw(STR(FTP_GIVEPWORD)" Specify password\r\n"); } else if (cmdval == const_PASS) { if (check_password(pw, G.ftp_arg) > 0) { break; /* login success */ @@ -1207,12 +1283,11 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) WRITE_OK(FTP_GOODBYE); return 0; } else { - cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n"); + cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER+PASS\r\n"); } } - change_identity(pw); + WRITE_OK(FTP_LOGINOK); } - WRITE_OK(FTP_LOGINOK); #endif /* Do this after auth, else /etc/passwd is not accessible */ @@ -1221,13 +1296,34 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) #endif argv += optind; if (argv[0]) { + const char *basedir = argv[0]; #if !BB_MMU G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); close_on_exec_on(G.root_fd); #endif - xchroot(argv[0]); + if (chroot(basedir) == 0) + basedir = "/"; +#if !BB_MMU + else { + close(G.root_fd); + G.root_fd = -1; + } +#endif + /* + * If chroot failed, assume that we aren't root, + * and at least chdir to the specified DIR + * (older versions were dying with error message). + * If chroot worked, move current dir to new "/": + */ + xchdir(basedir); } +#if ENABLE_FEATURE_FTPD_AUTHENTICATION + if (pw) + change_identity(pw); + /* else: -A is in effect */ +#endif + /* RFC-959 Section 5.1 * The following commands and options MUST be supported by every * server-FTP and user-FTP, except in cases where the underlying @@ -1331,7 +1427,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) handle_port(); else if (cmdval == const_REST) handle_rest(); -#if ENABLE_FEATURE_FTP_WRITE +#if ENABLE_FEATURE_FTPD_WRITE else if (opts & OPT_w) { if (cmdval == const_STOR) handle_stor(); @@ -1371,7 +1467,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) * (doesn't necessarily mean "we must support them") * foo 1.2.3: XXXX - comment */ -#if ENABLE_FEATURE_FTP_WRITE +#if ENABLE_FEATURE_FTPD_WRITE bad_cmd: #endif cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");