1 /* vi: set sw=4 ts=4: */
3 * Simple FTP daemon, based on vsftpd 2.0.7 (written by Chris Evans)
5 * Author: Adam Tkac <vonsch@gmail.com>
7 * Licensed under GPLv2, see file LICENSE in this source tree.
9 * Only subset of FTP protocol is implemented but vast majority of clients
10 * should not have any problem.
12 * You have to run this daemon via inetd.
15 //config: bool "ftpd (30 kb)"
18 //config: Simple FTP daemon. You have to run it via inetd.
20 //config:config FEATURE_FTPD_WRITE
21 //config: bool "Enable -w (upload commands)"
23 //config: depends on FTPD
25 //config: Enable -w option. "ftpd -w" will accept upload commands
26 //config: such as STOR, STOU, APPE, DELE, MKD, RMD, rename commands.
28 //config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST
29 //config: bool "Enable workaround for RFC-violating clients"
31 //config: depends on FTPD
33 //config: Some ftp clients (among them KDE's Konqueror) issue illegal
34 //config: "LIST -l" requests. This option works around such problems.
35 //config: It might prevent you from listing files starting with "-" and
36 //config: it increases the code size by ~40 bytes.
37 //config: Most other ftp servers seem to behave similar to this.
39 //config:config FEATURE_FTPD_AUTHENTICATION
40 //config: bool "Enable authentication"
42 //config: depends on FTPD
44 //config: Require login, and change to logged in user's UID:GID before
45 //config: accessing any files. Option "-a USER" allows "anonymous"
46 //config: logins (treats them as if USER logged in).
48 //config: If this option is not selected, ftpd runs with the rights
49 //config: of the user it was started under, and does not require login.
50 //config: Take care to not launch it under root.
52 //applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
54 //kbuild:lib-$(CONFIG_FTPD) += ftpd.o
56 //usage:#define ftpd_trivial_usage
57 //usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t N] [-T N] [DIR]"
58 //usage:#define ftpd_full_usage "\n\n"
59 //usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION(
60 //usage: "Anonymous FTP server. Client access occurs under ftpd's UID.\n"
62 //usage: IF_FEATURE_FTPD_AUTHENTICATION(
63 //usage: "FTP server. "
65 //usage: "Chroots to DIR, if this fails (run by non-root), cds to it.\n"
66 //usage: "Should be used as inetd service, inetd.conf line:\n"
67 //usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n"
68 //usage: "Can be run from tcpsvd:\n"
69 //usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve"
71 //usage: "\n -w Allow upload"
72 //usage: IF_FEATURE_FTPD_AUTHENTICATION(
73 //usage: "\n -A No login required, client access occurs under ftpd's UID"
74 //usage: "\n -a USER Enable 'anonymous' login and map it to USER"
76 //usage: "\n -v Log errors to stderr. -vv: verbose log"
77 //usage: "\n -S Log errors to syslog. -SS: verbose log"
78 //usage: "\n -t,-T N Idle and absolute timeout"
81 #include "common_bufsiz.h"
83 #include <netinet/tcp.h>
85 #define FTP_DATACONN 150
86 #define FTP_NOOPOK 200
87 #define FTP_TYPEOK 200
88 #define FTP_PORTOK 200
89 #define FTP_STRUOK 200
90 #define FTP_MODEOK 200
91 #define FTP_ALLOOK 202
92 #define FTP_STATOK 211
93 #define FTP_STATFILE_OK 213
95 #define FTP_SYSTOK 215
97 #define FTP_GOODBYE 221
98 #define FTP_TRANSFEROK 226
99 #define FTP_PASVOK 227
100 /*#define FTP_EPRTOK 228*/
101 #define FTP_EPSVOK 229
102 #define FTP_LOGINOK 230
103 #define FTP_CWDOK 250
104 #define FTP_RMDIROK 250
105 #define FTP_DELEOK 250
106 #define FTP_RENAMEOK 250
107 #define FTP_PWDOK 257
108 #define FTP_MKDIROK 257
109 #define FTP_GIVEPWORD 331
110 #define FTP_RESTOK 350
111 #define FTP_RNFROK 350
112 #define FTP_TIMEOUT 421
113 #define FTP_BADSENDCONN 425
114 #define FTP_BADSENDNET 426
115 #define FTP_BADSENDFILE 451
116 #define FTP_BADCMD 500
117 #define FTP_COMMANDNOTIMPL 502
118 #define FTP_NEEDUSER 503
119 #define FTP_NEEDRNFR 503
120 #define FTP_BADSTRU 504
121 #define FTP_BADMODE 504
122 #define FTP_LOGINERR 530
123 #define FTP_FILEFAIL 550
124 #define FTP_NOPERM 550
125 #define FTP_UPLOADFAIL 553
128 #define STR(s) STR1(s)
130 /* Convert a constant to 3-digit string, packed into uint32_t */
132 /* Shift for Nth decimal digit */
133 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
134 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
135 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
136 /* And for 4th position (space) */
137 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
139 #define STRNUM32(s) (uint32_t)(0 \
140 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
141 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
142 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
144 #define STRNUM32sp(s) (uint32_t)(0 \
146 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
147 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
148 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
151 #define MSG_OK "Operation successful\r\n"
152 #define MSG_ERR "Error\r\n"
163 off_t local_file_pos;
165 len_and_sockaddr *local_addr;
166 len_and_sockaddr *port_addr;
169 #if ENABLE_FEATURE_FTPD_WRITE
172 /* We need these aligned to uint32_t */
173 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
174 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
176 #define G (*ptr_to_globals)
177 /* ^^^ about 75 bytes smaller code than this: */
178 //#define G (*(struct globals*)bb_common_bufsiz1)
179 #define INIT_G() do { \
180 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
181 /*setup_common_bufsiz();*/ \
183 /* Moved to main */ \
184 /*strcpy(G.msg_ok + 4, MSG_OK );*/ \
185 /*strcpy(G.msg_err + 4, MSG_ERR);*/ \
190 escape_text(const char *prepend, const char *str, unsigned escapee)
192 unsigned retlen, remainlen, chunklen;
196 append = (char)escapee;
199 remainlen = strlen(str);
200 retlen = strlen(prepend);
201 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
202 strcpy(ret, prepend);
205 found = strchrnul(str, escapee);
206 chunklen = found - str + 1;
208 /* Copy chunk up to and including escapee (or NUL) to ret */
209 memcpy(ret + retlen, str, chunklen);
212 if (*found == '\0') {
213 /* It wasn't escapee, it was NUL! */
214 ret[retlen - 1] = append; /* replace NUL */
215 ret[retlen] = '\0'; /* add NUL */
218 ret[retlen++] = escapee; /* duplicate escapee */
224 /* Returns strlen as a bonus */
226 replace_char(char *str, char from, char to)
238 verbose_log(const char *str)
240 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
243 /* NB: status_str is char[4] packed into uint32_t */
245 cmdio_write(uint32_t status_str, const char *str)
250 /* FTP uses telnet protocol for command link.
251 * In telnet, 0xff is an escape char, and needs to be escaped: */
252 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
254 /* FTP sends embedded LFs as NULs */
255 len = replace_char(response, '\n', '\0');
257 response[len++] = '\n'; /* tack on trailing '\n' */
258 xwrite(STDOUT_FILENO, response, len);
260 verbose_log(response);
265 cmdio_write_ok(unsigned status)
267 *(bb__aliased_uint32_t *) G.msg_ok = status;
268 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
270 verbose_log(G.msg_ok);
272 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
274 /* TODO: output strerr(errno) if errno != 0? */
276 cmdio_write_error(unsigned status)
278 *(bb__aliased_uint32_t *) G.msg_err = status;
279 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
281 verbose_log(G.msg_err);
283 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
286 cmdio_write_raw(const char *p_text)
288 xwrite_str(STDOUT_FILENO, p_text);
294 timeout_handler(int sig UNUSED_PARAM)
297 int sv_errno = errno;
299 if ((int)(monotonic_sec() - G.end_time) >= 0)
302 if (!G.local_file_fd)
305 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
306 if (pos == G.local_file_pos)
308 G.local_file_pos = pos;
315 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
316 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
320 /* Simple commands */
325 char *cwd, *response;
327 cwd = xrealloc_getcwd_or_warn(NULL);
331 /* We have to promote each " to "" */
332 response = escape_text(" \"", cwd, ('"' << 8) + '"');
334 cmdio_write(STRNUM32(FTP_PWDOK), response);
341 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
342 WRITE_ERR(FTP_FILEFAIL);
351 G.ftp_arg = (char*)"..";
358 cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
360 STR(FTP_STATOK)" Ok\r\n");
363 /* Examples of HELP and FEAT:
364 # nc -vvv ftp.kernel.org 21
365 ftp.kernel.org (130.239.17.4:21) open
366 220 Welcome to ftp.kernel.org.
379 214-The following commands are recognized.
380 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
381 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
382 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
387 handle_feat(unsigned status)
389 cmdio_write(status, "-Features:");
390 cmdio_write_raw(" EPSV\r\n"
395 cmdio_write(status, " Ok");
398 /* Download commands */
403 return (G.port_addr != NULL);
409 return (G.pasv_listen_fd > STDOUT_FILENO);
413 port_pasv_cleanup(void)
417 if (G.pasv_listen_fd > STDOUT_FILENO)
418 close(G.pasv_listen_fd);
419 G.pasv_listen_fd = -1;
422 /* On error, emits error code to the peer */
424 ftpdataio_get_pasv_fd(void)
428 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
431 WRITE_ERR(FTP_BADSENDCONN);
435 setsockopt_keepalive(remote_fd);
439 /* Clears port/pasv data.
440 * This means we dont waste resources, for example, keeping
441 * PASV listening socket open when it is no longer needed.
442 * On error, emits error code to the peer (or exits).
443 * On success, emits p_status_msg to the peer.
446 get_remote_transfer_fd(const char *p_status_msg)
451 /* On error, emits error code to the peer */
452 remote_fd = ftpdataio_get_pasv_fd();
455 remote_fd = xconnect_stream(G.port_addr);
462 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
466 /* If there were neither PASV nor PORT, emits error code to the peer */
468 port_or_pasv_was_seen(void)
470 if (!pasv_active() && !port_active()) {
471 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
480 bind_for_passive_mode(void)
487 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
488 setsockopt_reuseaddr(fd);
490 set_nport(&G.local_addr->u.sa, 0);
491 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
493 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
495 port = get_nport(&G.local_addr->u.sa);
505 char *addr, *response;
507 port = bind_for_passive_mode();
509 if (G.local_addr->u.sa.sa_family == AF_INET)
510 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
511 else /* seen this in the wild done by other ftp servers: */
512 addr = xstrdup("0.0.0.0");
513 replace_char(addr, '.', ',');
515 response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
516 addr, (int)(port >> 8), (int)(port & 255));
518 cmdio_write_raw(response);
529 port = bind_for_passive_mode();
530 response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
531 cmdio_write_raw(response);
538 unsigned port, port_hi;
540 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
541 socklen_t peer_ipv4_len;
542 struct sockaddr_in peer_ipv4;
543 struct in_addr port_ipv4_sin_addr;
550 /* PORT command format makes sense only over IPv4 */
552 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
553 || G.local_addr->u.sa.sa_family != AF_INET
557 WRITE_ERR(FTP_BADCMD);
561 comma = strrchr(raw, ',');
565 port = bb_strtou(&comma[1], NULL, 10);
566 if (errno || port > 0xff)
569 comma = strrchr(raw, ',');
573 port_hi = bb_strtou(&comma[1], NULL, 10);
574 if (errno || port_hi > 0xff)
576 port |= port_hi << 8;
578 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
579 replace_char(raw, ',', '.');
581 /* We are verifying that PORT's IP matches getpeername().
582 * Otherwise peer can make us open data connections
583 * to other hosts (security problem!)
584 * This code would be too simplistic:
585 * lsa = xdotted2sockaddr(raw, port);
586 * if (lsa == NULL) goto bail;
588 if (!inet_aton(raw, &port_ipv4_sin_addr))
590 peer_ipv4_len = sizeof(peer_ipv4);
591 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
593 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
596 G.port_addr = xdotted2sockaddr(raw, port);
598 G.port_addr = get_peer_lsa(STDIN_FILENO);
599 set_nport(&G.port_addr->u.sa, htons(port));
601 WRITE_OK(FTP_PORTOK);
607 /* When ftp_arg == NULL simply restart from beginning */
608 G.restart_pos = G.ftp_arg ? XATOOFF(G.ftp_arg) : 0;
609 WRITE_OK(FTP_RESTOK);
616 off_t bytes_transferred;
619 off_t offset = G.restart_pos;
624 if (!port_or_pasv_was_seen())
625 return; /* port_or_pasv_was_seen emitted error response */
627 /* O_NONBLOCK is useful if file happens to be a device node */
628 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
629 if (local_file_fd < 0) {
630 WRITE_ERR(FTP_FILEFAIL);
634 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
635 /* Note - pretend open failed */
636 WRITE_ERR(FTP_FILEFAIL);
639 G.local_file_fd = local_file_fd;
641 /* Now deactive O_NONBLOCK, otherwise we have a problem
642 * on DMAPI filesystems such as XFS DMAPI.
644 ndelay_off(local_file_fd);
646 /* Set the download offset (from REST) if any */
648 xlseek(local_file_fd, offset, SEEK_SET);
650 response = xasprintf(
651 " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
652 G.ftp_arg, statbuf.st_size);
653 remote_fd = get_remote_transfer_fd(response);
658 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
660 if (bytes_transferred < 0)
661 WRITE_ERR(FTP_BADSENDFILE);
663 WRITE_OK(FTP_TRANSFEROK);
666 close(local_file_fd);
673 popen_ls(const char *opt)
676 struct fd_pair outfd;
680 argv[1] = opt; /* "-lA" or "-1A" */
685 /* Improve compatibility with non-RFC conforming FTP clients
686 * which send e.g. "LIST -l", "LIST -la", "LIST -aL".
687 * See https://bugs.kde.org/show_bug.cgi?id=195578 */
688 if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
689 && G.ftp_arg && G.ftp_arg[0] == '-'
691 const char *tmp = strchr(G.ftp_arg, ' ');
692 if (tmp) /* skip the space */
699 /*fflush_all(); - so far we dont use stdio on output */
700 pid = BB_MMU ? xfork() : xvfork();
706 /* NB: close _first_, then move fd! */
708 xmove_fd(outfd.wr, STDOUT_FILENO);
709 /* Opening /dev/null in chroot is hard.
710 * Just making sure STDIN_FILENO is opened
711 * to something harmless. Paranoia,
712 * ls won't read it anyway */
714 dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */
716 /* memset(&G, 0, sizeof(G)); - ls_main does it */
717 exit(ls_main(/*argc_unused*/ 0, (char**) argv));
719 cur_fd = xopen(".", O_RDONLY | O_DIRECTORY);
720 /* On NOMMU, we want to execute a child - copy of ourself
721 * in order to unblock parent after vfork.
722 * In chroot we usually can't re-exec. Thus we escape
723 * out of the chroot back to original root.
725 if (G.root_fd >= 0) {
726 if (fchdir(G.root_fd) != 0 || chroot(".") != 0)
728 /*close(G.root_fd); - close_on_exec_on() took care of this */
730 /* Child expects directory to list on fd #3 */
732 execv(bb_busybox_exec_path, (char**) argv);
748 handle_dir_common(int opts)
754 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
755 return; /* port_or_pasv_was_seen emitted error response */
757 ls_fd = popen_ls((opts & LONG_LISTING) ? "-lA" : "-1A");
758 ls_fp = xfdopen_for_read(ls_fd);
759 /* FIXME: filenames with embedded newlines are mishandled */
761 if (opts & USE_CTRL_CONN) {
762 /* STAT <filename> */
763 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
765 line = xmalloc_fgetline(ls_fp);
768 /* Hack: 0 results in no status at all */
769 /* Note: it's ok that we don't prepend space,
770 * ftp.kernel.org doesn't do that too */
771 cmdio_write(0, line);
774 WRITE_OK(FTP_STATFILE_OK);
776 /* LIST/NLST [<filename>] */
777 int remote_fd = get_remote_transfer_fd(" Directory listing");
778 if (remote_fd >= 0) {
782 line = xmalloc_fgets(ls_fp);
785 /* I've seen clients complaining when they
786 * are fed with ls output with bare '\n'.
787 * Replace trailing "\n\0" with "\r\n".
790 if (len != 0) /* paranoia check */
791 line[len - 1] = '\r';
793 xwrite(remote_fd, line, len + 1);
798 WRITE_OK(FTP_TRANSFEROK);
800 fclose(ls_fp); /* closes ls_fd too */
805 handle_dir_common(LONG_LISTING);
810 /* NLST returns list of names, "\r\n" terminated without regard
811 * to the current binary flag. Names may start with "/",
812 * then they represent full names (we don't produce such names),
813 * otherwise names are relative to current directory.
814 * Embedded "\n" are replaced by NULs. This is safe since names
815 * can never contain NUL.
817 handle_dir_common(0);
820 handle_stat_file(void)
822 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
825 /* This can be extended to handle MLST, as all info is available
826 * in struct stat for that:
828 * 250-Listing file_name
829 * type=file;size=4161;modify=19970214165800; /dir/dir/file_name
832 * MLST [<file or dir name, "." assumed if not given>]
833 * Returned name should be either the same as requested, or fully qualified.
834 * If there was no parameter, return "" or (preferred) fully-qualified name.
835 * Returned "facts" (case is not important):
836 * size - size in octets
837 * modify - last modification time
838 * type - entry type (file,dir,OS.unix=block)
839 * (+ cdir and pdir types for MLSD)
840 * unique - unique id of file/directory (inode#)
842 * a: can be appended to (APPE)
843 * d: can be deleted (RMD/DELE)
844 * f: can be renamed (RNFR)
845 * r: can be read (RETR)
846 * w: can be written (STOR)
847 * e: can CWD into this dir
848 * l: this dir can be listed (dir only!)
849 * c: can create files in this dir
850 * m: can create dirs in this dir (MKD)
851 * p: can delete files in this dir
852 * UNIX.mode - unix file mode
855 handle_size_or_mdtm(int need_size)
858 struct tm broken_out;
859 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
860 | sizeof("NNN YYYYMMDDhhmmss\r\n")
864 || stat(G.ftp_arg, &statbuf) != 0
865 || !S_ISREG(statbuf.st_mode)
867 WRITE_ERR(FTP_FILEFAIL);
871 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
873 gmtime_r(&statbuf.st_mtime, &broken_out);
874 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
875 broken_out.tm_year + 1900,
876 broken_out.tm_mon + 1,
882 cmdio_write_raw(buf);
885 /* Upload commands */
887 #if ENABLE_FEATURE_FTPD_WRITE
891 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
892 WRITE_ERR(FTP_FILEFAIL);
895 WRITE_OK(FTP_MKDIROK);
901 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
902 WRITE_ERR(FTP_FILEFAIL);
905 WRITE_OK(FTP_RMDIROK);
911 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
912 WRITE_ERR(FTP_FILEFAIL);
915 WRITE_OK(FTP_DELEOK);
921 free(G.rnfr_filename);
922 G.rnfr_filename = xstrdup(G.ftp_arg);
923 WRITE_OK(FTP_RNFROK);
931 /* If we didn't get a RNFR, throw a wobbly */
932 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
933 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
937 retval = rename(G.rnfr_filename, G.ftp_arg);
938 free(G.rnfr_filename);
939 G.rnfr_filename = NULL;
942 WRITE_ERR(FTP_FILEFAIL);
945 WRITE_OK(FTP_RENAMEOK);
949 handle_upload_common(int is_append, int is_unique)
953 off_t bytes_transferred;
958 offset = G.restart_pos;
961 if (!port_or_pasv_was_seen())
962 return; /* port_or_pasv_was_seen emitted error response */
967 tempname = xstrdup(" FILE: uniq.XXXXXX");
968 local_file_fd = mkstemp(tempname + 7);
969 } else if (G.ftp_arg) {
970 int flags = O_WRONLY | O_CREAT | O_TRUNC;
972 flags = O_WRONLY | O_CREAT | O_APPEND;
974 flags = O_WRONLY | O_CREAT;
975 local_file_fd = open(G.ftp_arg, flags, 0666);
978 if (local_file_fd < 0
979 || fstat(local_file_fd, &statbuf) != 0
980 || !S_ISREG(statbuf.st_mode)
983 WRITE_ERR(FTP_UPLOADFAIL);
984 if (local_file_fd >= 0)
985 goto close_local_and_bail;
988 G.local_file_fd = local_file_fd;
991 xlseek(local_file_fd, offset, SEEK_SET);
993 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
997 goto close_local_and_bail;
999 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
1001 if (bytes_transferred < 0)
1002 WRITE_ERR(FTP_BADSENDFILE);
1004 WRITE_OK(FTP_TRANSFEROK);
1006 close_local_and_bail:
1007 close(local_file_fd);
1008 G.local_file_fd = 0;
1014 handle_upload_common(0, 0);
1021 handle_upload_common(1, 0);
1028 handle_upload_common(0, 1);
1030 #endif /* ENABLE_FEATURE_FTPD_WRITE */
1033 cmdio_get_cmd_and_arg(void)
1043 /* Paranoia. Peer may send 1 gigabyte long cmd... */
1044 /* Using separate len_on_stk instead of len optimizes
1045 * code size (allows len to be in CPU register) */
1046 size_t len_on_stk = 8 * 1024;
1047 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
1053 /* De-escape telnet: 0xff,0xff => 0xff */
1054 /* RFC959 says that ABOR, STAT, QUIT may be sent even during
1055 * data transfer, and may be preceded by telnet's "Interrupt Process"
1056 * code (two-byte sequence 255,244) and then by telnet "Synch" code
1057 * 255,242 (byte 242 is sent with TCP URG bit using send(MSG_OOB)
1058 * and may generate SIGURG on our side. See RFC854).
1059 * So far we don't support that (may install SIGURG handler if we'd want to),
1060 * but we need to at least remove 255,xxx pairs. lftp sends those. */
1061 /* Then de-escape FTP: NUL => '\n' */
1062 /* Testing for \xff:
1063 * Create file named '\xff': echo Hello >`echo -ne "\xff"`
1064 * Try to get it: ftpget -v 127.0.0.1 Eff `echo -ne "\xff\xff"`
1065 * (need "\xff\xff" until ftpget applet is fixed to do escaping :)
1066 * Testing for embedded LF:
1067 * LF_HERE=`echo -ne "LF\nHERE"`
1068 * echo Hello >"$LF_HERE"
1069 * ftpget -v 127.0.0.1 LF_HERE "$LF_HERE"
1074 /* Strip "\r\n" if it is there */
1075 if (len != 0 && cmd[len - 1] == '\n') {
1077 if (len != 0 && cmd[len - 1] == '\r')
1081 src = strchrnul(cmd, 0xff) - cmd;
1082 /* 99,99% there are neither NULs nor 255s and src == len */
1086 if ((unsigned char)(cmd[src]) == 255) {
1088 /* 255,xxx - skip 255 */
1089 if ((unsigned char)(cmd[src]) != 255) {
1090 /* 255,!255 - skip both */
1094 /* 255,255 - retain one 255 */
1097 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1099 } while (src < len);
1107 G.ftp_arg = strchr(cmd, ' ');
1108 if (G.ftp_arg != NULL)
1109 *G.ftp_arg++ = '\0';
1111 /* Uppercase and pack into uint32_t first word of the command */
1114 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1119 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1120 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1122 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1123 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1124 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1125 const_CWD = mk_const3('C', 'W', 'D'),
1126 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1127 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1128 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1129 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1130 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1131 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1132 const_MKD = mk_const3('M', 'K', 'D'),
1133 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1134 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1135 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1136 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1137 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1138 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1139 const_PWD = mk_const3('P', 'W', 'D'),
1140 /* Same as PWD. Reportedly used by windows ftp client */
1141 const_XPWD = mk_const4('X', 'P', 'W', 'D'),
1142 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1143 const_REST = mk_const4('R', 'E', 'S', 'T'),
1144 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1145 const_RMD = mk_const3('R', 'M', 'D'),
1146 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1147 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1148 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1149 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1150 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1151 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1152 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1153 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1154 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1155 const_USER = mk_const4('U', 'S', 'E', 'R'),
1162 BIT_v = (!BB_MMU) * 3,
1163 OPT_v = (1 << (BIT_v + 0)),
1164 OPT_S = (1 << (BIT_v + 1)),
1165 OPT_w = (1 << (BIT_v + 2)) * ENABLE_FEATURE_FTPD_WRITE,
1166 BIT_A = BIT_v + 2 + ENABLE_FEATURE_FTPD_WRITE,
1167 OPT_A = (1 << (BIT_A + 0)) * ENABLE_FEATURE_FTPD_AUTHENTICATION,
1170 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1171 int ftpd_main(int argc UNUSED_PARAM, char **argv)
1173 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1174 struct passwd *pw = NULL;
1175 char *anon_opt = NULL;
1177 unsigned abs_timeout;
1183 abs_timeout = 1 * 60 * 60;
1187 opts = getopt32(argv, "^" "vS"
1188 IF_FEATURE_FTPD_WRITE("w") IF_FEATURE_FTPD_AUTHENTICATION("A")
1189 "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1191 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1192 &G.verbose, &verbose_S
1195 opts = getopt32(argv, "^" "l1AvS"
1196 IF_FEATURE_FTPD_WRITE("w") IF_FEATURE_FTPD_AUTHENTICATION("A")
1197 "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1199 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1200 &G.verbose, &verbose_S
1202 if (opts & (OPT_l|OPT_1)) {
1203 /* Our secret backdoor to ls */
1206 /* memset(&G, 0, sizeof(G)); - ls_main does it */
1207 return ls_main(/*argc_unused*/ 0, argv);
1210 if (G.verbose < verbose_S)
1211 G.verbose = verbose_S;
1212 if (abs_timeout | G.timeout) {
1213 if (abs_timeout == 0)
1214 abs_timeout = INT_MAX;
1215 G.end_time = monotonic_sec() + abs_timeout;
1216 if (G.timeout > abs_timeout)
1217 G.timeout = abs_timeout;
1219 strcpy(G.msg_ok + 4, MSG_OK );
1220 strcpy(G.msg_err + 4, MSG_ERR);
1222 G.local_addr = get_sock_lsa(STDIN_FILENO);
1223 if (!G.local_addr) {
1224 /* This is confusing:
1225 * bb_error_msg_and_die("stdin is not a socket");
1228 /* Help text says that ftpd must be used as inetd service,
1229 * which is by far the most usual cause of get_sock_lsa
1233 if (!(opts & OPT_v))
1234 logmode = LOGMODE_NONE;
1236 /* LOG_NDELAY is needed since we may chroot later */
1237 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1238 logmode |= LOGMODE_SYSLOG;
1241 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1243 //umask(077); - admin can set umask before starting us
1247 /* We'll always take EPIPE rather than a rude signal, thanks */
1249 /* LIST command spawns chilren. Prevent zombies */
1253 /* Set up options on the command socket (do we need these all? why?) */
1254 setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY);
1255 setsockopt_keepalive(STDIN_FILENO);
1256 /* Telnet protocol over command link may send "urgent" data,
1257 * we prefer it to be received in the "normal" data stream: */
1258 setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE);
1260 WRITE_OK(FTP_GREET);
1261 signal(SIGALRM, timeout_handler);
1263 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1264 if (!(opts & OPT_A)) {
1266 uint32_t cmdval = cmdio_get_cmd_and_arg();
1267 if (cmdval == const_USER) {
1268 if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) {
1269 pw = getpwnam(anon_opt);
1271 break; /* does not even ask for password */
1273 pw = getpwnam(G.ftp_arg);
1274 cmdio_write_raw(STR(FTP_GIVEPWORD)" Specify password\r\n");
1275 } else if (cmdval == const_PASS) {
1276 if (check_password(pw, G.ftp_arg) > 0) {
1277 break; /* login success */
1279 cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1281 } else if (cmdval == const_QUIT) {
1282 WRITE_OK(FTP_GOODBYE);
1285 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER+PASS\r\n");
1288 WRITE_OK(FTP_LOGINOK);
1292 /* Do this after auth, else /etc/passwd is not accessible */
1298 const char *basedir = argv[0];
1300 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1301 close_on_exec_on(G.root_fd);
1303 if (chroot(basedir) == 0)
1312 * If chroot failed, assume that we aren't root,
1313 * and at least chdir to the specified DIR
1314 * (older versions were dying with error message).
1315 * If chroot worked, move current dir to new "/":
1320 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1322 change_identity(pw);
1323 /* else: -A is in effect */
1326 /* RFC-959 Section 5.1
1327 * The following commands and options MUST be supported by every
1328 * server-FTP and user-FTP, except in cases where the underlying
1329 * file system or operating system does not allow or support
1330 * a particular command.
1331 * Type: ASCII Non-print, IMAGE, LOCAL 8
1333 * Structure: File, Record*
1334 * (Record structure is REQUIRED only for hosts whose file
1335 * systems support record structure).
1337 * USER, PASS, ACCT, [bbox: ACCT not supported]
1342 * CWD, CDUP, RMD, MKD, PWD,
1348 * "The argument field is a Telnet string identifying the user's account.
1349 * The command is not necessarily related to the USER command, as some
1350 * sites may require an account for login and others only for specific
1351 * access, such as storing files. In the latter case the command may
1352 * arrive at any time.
1353 * There are reply codes to differentiate these cases for the automation:
1354 * when account information is required for login, the response to
1355 * a successful PASSword command is reply code 332. On the other hand,
1356 * if account information is NOT required for login, the reply to
1357 * a successful PASSword command is 230; and if the account information
1358 * is needed for a command issued later in the dialogue, the server
1359 * should return a 332 or 532 reply depending on whether it stores
1360 * (pending receipt of the ACCounT command) or discards the command,
1365 uint32_t cmdval = cmdio_get_cmd_and_arg();
1367 if (cmdval == const_QUIT) {
1368 WRITE_OK(FTP_GOODBYE);
1371 else if (cmdval == const_USER)
1372 /* This would mean "ok, now give me PASS". */
1373 /*WRITE_OK(FTP_GIVEPWORD);*/
1374 /* vsftpd can be configured to not require that,
1375 * and this also saves one roundtrip:
1377 WRITE_OK(FTP_LOGINOK);
1378 else if (cmdval == const_PASS)
1379 WRITE_OK(FTP_LOGINOK);
1380 else if (cmdval == const_NOOP)
1381 WRITE_OK(FTP_NOOPOK);
1382 else if (cmdval == const_TYPE)
1383 WRITE_OK(FTP_TYPEOK);
1384 else if (cmdval == const_STRU)
1385 WRITE_OK(FTP_STRUOK);
1386 else if (cmdval == const_MODE)
1387 WRITE_OK(FTP_MODEOK);
1388 else if (cmdval == const_ALLO)
1389 WRITE_OK(FTP_ALLOOK);
1390 else if (cmdval == const_SYST)
1391 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1392 else if (cmdval == const_PWD || cmdval == const_XPWD)
1394 else if (cmdval == const_CWD)
1396 else if (cmdval == const_CDUP) /* cd .. */
1398 /* HELP is nearly useless, but we can reuse FEAT for it */
1399 /* lftp uses FEAT */
1400 else if (cmdval == const_HELP || cmdval == const_FEAT)
1401 handle_feat(cmdval == const_HELP
1402 ? STRNUM32(FTP_HELP)
1403 : STRNUM32(FTP_STATOK)
1405 else if (cmdval == const_LIST) /* ls -l */
1407 else if (cmdval == const_NLST) /* "name list", bare ls */
1409 /* SIZE is crucial for wget's download indicator etc */
1410 /* Mozilla, lftp use MDTM (presumably for caching) */
1411 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1412 handle_size_or_mdtm(cmdval == const_SIZE);
1413 else if (cmdval == const_STAT) {
1414 if (G.ftp_arg == NULL)
1419 else if (cmdval == const_PASV)
1421 else if (cmdval == const_EPSV)
1423 else if (cmdval == const_RETR)
1425 else if (cmdval == const_PORT)
1427 else if (cmdval == const_REST)
1429 #if ENABLE_FEATURE_FTPD_WRITE
1430 else if (opts & OPT_w) {
1431 if (cmdval == const_STOR)
1433 else if (cmdval == const_MKD)
1435 else if (cmdval == const_RMD)
1437 else if (cmdval == const_DELE)
1439 else if (cmdval == const_RNFR) /* "rename from" */
1441 else if (cmdval == const_RNTO) /* "rename to" */
1443 else if (cmdval == const_APPE)
1445 else if (cmdval == const_STOU) /* "store unique" */
1452 else if (cmdval == const_STOR
1453 || cmdval == const_MKD
1454 || cmdval == const_RMD
1455 || cmdval == const_DELE
1456 || cmdval == const_RNFR
1457 || cmdval == const_RNTO
1458 || cmdval == const_APPE
1459 || cmdval == const_STOU
1461 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1465 /* Which unsupported commands were seen in the wild?
1466 * (doesn't necessarily mean "we must support them")
1467 * foo 1.2.3: XXXX - comment
1469 #if ENABLE_FEATURE_FTPD_WRITE
1472 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");