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. Accesses by clients occur 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\n"
70 //usage: "\n -w Allow upload"
71 //usage: IF_FEATURE_FTPD_AUTHENTICATION(
72 //usage: "\n -a USER Enable 'anonymous' login and map it to USER"
74 //usage: "\n -v Log errors to stderr. -vv: verbose log"
75 //usage: "\n -S Log errors to syslog. -SS: verbose log"
76 //usage: "\n -t,-T N Idle and absolute timeout"
79 #include "common_bufsiz.h"
81 #include <netinet/tcp.h>
83 #define FTP_DATACONN 150
84 #define FTP_NOOPOK 200
85 #define FTP_TYPEOK 200
86 #define FTP_PORTOK 200
87 #define FTP_STRUOK 200
88 #define FTP_MODEOK 200
89 #define FTP_ALLOOK 202
90 #define FTP_STATOK 211
91 #define FTP_STATFILE_OK 213
93 #define FTP_SYSTOK 215
95 #define FTP_GOODBYE 221
96 #define FTP_TRANSFEROK 226
97 #define FTP_PASVOK 227
98 /*#define FTP_EPRTOK 228*/
99 #define FTP_EPSVOK 229
100 #define FTP_LOGINOK 230
101 #define FTP_CWDOK 250
102 #define FTP_RMDIROK 250
103 #define FTP_DELEOK 250
104 #define FTP_RENAMEOK 250
105 #define FTP_PWDOK 257
106 #define FTP_MKDIROK 257
107 #define FTP_GIVEPWORD 331
108 #define FTP_RESTOK 350
109 #define FTP_RNFROK 350
110 #define FTP_TIMEOUT 421
111 #define FTP_BADSENDCONN 425
112 #define FTP_BADSENDNET 426
113 #define FTP_BADSENDFILE 451
114 #define FTP_BADCMD 500
115 #define FTP_COMMANDNOTIMPL 502
116 #define FTP_NEEDUSER 503
117 #define FTP_NEEDRNFR 503
118 #define FTP_BADSTRU 504
119 #define FTP_BADMODE 504
120 #define FTP_LOGINERR 530
121 #define FTP_FILEFAIL 550
122 #define FTP_NOPERM 550
123 #define FTP_UPLOADFAIL 553
126 #define STR(s) STR1(s)
128 /* Convert a constant to 3-digit string, packed into uint32_t */
130 /* Shift for Nth decimal digit */
131 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
132 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
133 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
134 /* And for 4th position (space) */
135 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
137 #define STRNUM32(s) (uint32_t)(0 \
138 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
139 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
140 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
142 #define STRNUM32sp(s) (uint32_t)(0 \
144 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
145 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
146 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
149 #define MSG_OK "Operation successful\r\n"
150 #define MSG_ERR "Error\r\n"
161 off_t local_file_pos;
163 len_and_sockaddr *local_addr;
164 len_and_sockaddr *port_addr;
167 #if ENABLE_FEATURE_FTPD_WRITE
170 /* We need these aligned to uint32_t */
171 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
172 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
174 #define G (*(struct globals*)bb_common_bufsiz1)
175 #define INIT_G() do { \
176 setup_common_bufsiz(); \
177 /* Moved to main */ \
178 /*strcpy(G.msg_ok + 4, MSG_OK );*/ \
179 /*strcpy(G.msg_err + 4, MSG_ERR);*/ \
184 escape_text(const char *prepend, const char *str, unsigned escapee)
186 unsigned retlen, remainlen, chunklen;
190 append = (char)escapee;
193 remainlen = strlen(str);
194 retlen = strlen(prepend);
195 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
196 strcpy(ret, prepend);
199 found = strchrnul(str, escapee);
200 chunklen = found - str + 1;
202 /* Copy chunk up to and including escapee (or NUL) to ret */
203 memcpy(ret + retlen, str, chunklen);
206 if (*found == '\0') {
207 /* It wasn't escapee, it was NUL! */
208 ret[retlen - 1] = append; /* replace NUL */
209 ret[retlen] = '\0'; /* add NUL */
212 ret[retlen++] = escapee; /* duplicate escapee */
218 /* Returns strlen as a bonus */
220 replace_char(char *str, char from, char to)
232 verbose_log(const char *str)
234 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
237 /* NB: status_str is char[4] packed into uint32_t */
239 cmdio_write(uint32_t status_str, const char *str)
244 /* FTP uses telnet protocol for command link.
245 * In telnet, 0xff is an escape char, and needs to be escaped: */
246 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
248 /* FTP sends embedded LFs as NULs */
249 len = replace_char(response, '\n', '\0');
251 response[len++] = '\n'; /* tack on trailing '\n' */
252 xwrite(STDOUT_FILENO, response, len);
254 verbose_log(response);
259 cmdio_write_ok(unsigned status)
261 *(uint32_t *) G.msg_ok = status;
262 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
264 verbose_log(G.msg_ok);
266 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
268 /* TODO: output strerr(errno) if errno != 0? */
270 cmdio_write_error(unsigned status)
272 *(uint32_t *) G.msg_err = status;
273 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
275 verbose_log(G.msg_err);
277 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
280 cmdio_write_raw(const char *p_text)
282 xwrite_str(STDOUT_FILENO, p_text);
288 timeout_handler(int sig UNUSED_PARAM)
291 int sv_errno = errno;
293 if ((int)(monotonic_sec() - G.end_time) >= 0)
296 if (!G.local_file_fd)
299 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
300 if (pos == G.local_file_pos)
302 G.local_file_pos = pos;
309 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
310 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
314 /* Simple commands */
319 char *cwd, *response;
321 cwd = xrealloc_getcwd_or_warn(NULL);
325 /* We have to promote each " to "" */
326 response = escape_text(" \"", cwd, ('"' << 8) + '"');
328 cmdio_write(STRNUM32(FTP_PWDOK), response);
335 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
336 WRITE_ERR(FTP_FILEFAIL);
345 G.ftp_arg = (char*)"..";
352 cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
354 STR(FTP_STATOK)" Ok\r\n");
357 /* Examples of HELP and FEAT:
358 # nc -vvv ftp.kernel.org 21
359 ftp.kernel.org (130.239.17.4:21) open
360 220 Welcome to ftp.kernel.org.
373 214-The following commands are recognized.
374 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
375 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
376 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
381 handle_feat(unsigned status)
383 cmdio_write(status, "-Features:");
384 cmdio_write_raw(" EPSV\r\n"
389 cmdio_write(status, " Ok");
392 /* Download commands */
397 return (G.port_addr != NULL);
403 return (G.pasv_listen_fd > STDOUT_FILENO);
407 port_pasv_cleanup(void)
411 if (G.pasv_listen_fd > STDOUT_FILENO)
412 close(G.pasv_listen_fd);
413 G.pasv_listen_fd = -1;
416 /* On error, emits error code to the peer */
418 ftpdataio_get_pasv_fd(void)
422 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
425 WRITE_ERR(FTP_BADSENDCONN);
429 setsockopt_keepalive(remote_fd);
433 /* Clears port/pasv data.
434 * This means we dont waste resources, for example, keeping
435 * PASV listening socket open when it is no longer needed.
436 * On error, emits error code to the peer (or exits).
437 * On success, emits p_status_msg to the peer.
440 get_remote_transfer_fd(const char *p_status_msg)
445 /* On error, emits error code to the peer */
446 remote_fd = ftpdataio_get_pasv_fd();
449 remote_fd = xconnect_stream(G.port_addr);
456 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
460 /* If there were neither PASV nor PORT, emits error code to the peer */
462 port_or_pasv_was_seen(void)
464 if (!pasv_active() && !port_active()) {
465 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
474 bind_for_passive_mode(void)
481 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
482 setsockopt_reuseaddr(fd);
484 set_nport(&G.local_addr->u.sa, 0);
485 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
487 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
489 port = get_nport(&G.local_addr->u.sa);
499 char *addr, *response;
501 port = bind_for_passive_mode();
503 if (G.local_addr->u.sa.sa_family == AF_INET)
504 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
505 else /* seen this in the wild done by other ftp servers: */
506 addr = xstrdup("0.0.0.0");
507 replace_char(addr, '.', ',');
509 response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
510 addr, (int)(port >> 8), (int)(port & 255));
512 cmdio_write_raw(response);
523 port = bind_for_passive_mode();
524 response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
525 cmdio_write_raw(response);
532 unsigned port, port_hi;
534 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
535 socklen_t peer_ipv4_len;
536 struct sockaddr_in peer_ipv4;
537 struct in_addr port_ipv4_sin_addr;
544 /* PORT command format makes sense only over IPv4 */
546 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
547 || G.local_addr->u.sa.sa_family != AF_INET
551 WRITE_ERR(FTP_BADCMD);
555 comma = strrchr(raw, ',');
559 port = bb_strtou(&comma[1], NULL, 10);
560 if (errno || port > 0xff)
563 comma = strrchr(raw, ',');
567 port_hi = bb_strtou(&comma[1], NULL, 10);
568 if (errno || port_hi > 0xff)
570 port |= port_hi << 8;
572 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
573 replace_char(raw, ',', '.');
575 /* We are verifying that PORT's IP matches getpeername().
576 * Otherwise peer can make us open data connections
577 * to other hosts (security problem!)
578 * This code would be too simplistic:
579 * lsa = xdotted2sockaddr(raw, port);
580 * if (lsa == NULL) goto bail;
582 if (!inet_aton(raw, &port_ipv4_sin_addr))
584 peer_ipv4_len = sizeof(peer_ipv4);
585 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
587 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
590 G.port_addr = xdotted2sockaddr(raw, port);
592 G.port_addr = get_peer_lsa(STDIN_FILENO);
593 set_nport(&G.port_addr->u.sa, htons(port));
595 WRITE_OK(FTP_PORTOK);
601 /* When ftp_arg == NULL simply restart from beginning */
602 G.restart_pos = G.ftp_arg ? xatoi_positive(G.ftp_arg) : 0;
603 WRITE_OK(FTP_RESTOK);
610 off_t bytes_transferred;
613 off_t offset = G.restart_pos;
618 if (!port_or_pasv_was_seen())
619 return; /* port_or_pasv_was_seen emitted error response */
621 /* O_NONBLOCK is useful if file happens to be a device node */
622 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
623 if (local_file_fd < 0) {
624 WRITE_ERR(FTP_FILEFAIL);
628 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
629 /* Note - pretend open failed */
630 WRITE_ERR(FTP_FILEFAIL);
633 G.local_file_fd = local_file_fd;
635 /* Now deactive O_NONBLOCK, otherwise we have a problem
636 * on DMAPI filesystems such as XFS DMAPI.
638 ndelay_off(local_file_fd);
640 /* Set the download offset (from REST) if any */
642 xlseek(local_file_fd, offset, SEEK_SET);
644 response = xasprintf(
645 " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
646 G.ftp_arg, statbuf.st_size);
647 remote_fd = get_remote_transfer_fd(response);
652 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
654 if (bytes_transferred < 0)
655 WRITE_ERR(FTP_BADSENDFILE);
657 WRITE_OK(FTP_TRANSFEROK);
660 close(local_file_fd);
667 popen_ls(const char *opt)
670 struct fd_pair outfd;
674 argv[1] = opt; /* "-lA" or "-1A" */
679 /* Improve compatibility with non-RFC conforming FTP clients
680 * which send e.g. "LIST -l", "LIST -la", "LIST -aL".
681 * See https://bugs.kde.org/show_bug.cgi?id=195578 */
682 if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
683 && G.ftp_arg && G.ftp_arg[0] == '-'
685 const char *tmp = strchr(G.ftp_arg, ' ');
686 if (tmp) /* skip the space */
693 /*fflush_all(); - so far we dont use stdio on output */
694 pid = BB_MMU ? xfork() : xvfork();
700 /* NB: close _first_, then move fd! */
702 xmove_fd(outfd.wr, STDOUT_FILENO);
703 /* Opening /dev/null in chroot is hard.
704 * Just making sure STDIN_FILENO is opened
705 * to something harmless. Paranoia,
706 * ls won't read it anyway */
708 dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */
710 /* memset(&G, 0, sizeof(G)); - ls_main does it */
711 exit(ls_main(/*argc_unused*/ 0, (char**) argv));
713 cur_fd = xopen(".", O_RDONLY | O_DIRECTORY);
714 /* On NOMMU, we want to execute a child - copy of ourself
715 * in order to unblock parent after vfork.
716 * In chroot we usually can't re-exec. Thus we escape
717 * out of the chroot back to original root.
719 if (G.root_fd >= 0) {
720 if (fchdir(G.root_fd) != 0 || chroot(".") != 0)
722 /*close(G.root_fd); - close_on_exec_on() took care of this */
724 /* Child expects directory to list on fd #3 */
726 execv(bb_busybox_exec_path, (char**) argv);
742 handle_dir_common(int opts)
748 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
749 return; /* port_or_pasv_was_seen emitted error response */
751 ls_fd = popen_ls((opts & LONG_LISTING) ? "-lA" : "-1A");
752 ls_fp = xfdopen_for_read(ls_fd);
753 /* FIXME: filenames with embedded newlines are mishandled */
755 if (opts & USE_CTRL_CONN) {
756 /* STAT <filename> */
757 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
759 line = xmalloc_fgetline(ls_fp);
762 /* Hack: 0 results in no status at all */
763 /* Note: it's ok that we don't prepend space,
764 * ftp.kernel.org doesn't do that too */
765 cmdio_write(0, line);
768 WRITE_OK(FTP_STATFILE_OK);
770 /* LIST/NLST [<filename>] */
771 int remote_fd = get_remote_transfer_fd(" Directory listing");
772 if (remote_fd >= 0) {
776 line = xmalloc_fgets(ls_fp);
779 /* I've seen clients complaining when they
780 * are fed with ls output with bare '\n'.
781 * Replace trailing "\n\0" with "\r\n".
784 if (len != 0) /* paranoia check */
785 line[len - 1] = '\r';
787 xwrite(remote_fd, line, len + 1);
792 WRITE_OK(FTP_TRANSFEROK);
794 fclose(ls_fp); /* closes ls_fd too */
799 handle_dir_common(LONG_LISTING);
804 /* NLST returns list of names, "\r\n" terminated without regard
805 * to the current binary flag. Names may start with "/",
806 * then they represent full names (we don't produce such names),
807 * otherwise names are relative to current directory.
808 * Embedded "\n" are replaced by NULs. This is safe since names
809 * can never contain NUL.
811 handle_dir_common(0);
814 handle_stat_file(void)
816 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
819 /* This can be extended to handle MLST, as all info is available
820 * in struct stat for that:
822 * 250-Listing file_name
823 * type=file;size=4161;modify=19970214165800; /dir/dir/file_name
826 * MLST [<file or dir name, "." assumed if not given>]
827 * Returned name should be either the same as requested, or fully qualified.
828 * If there was no parameter, return "" or (preferred) fully-qualified name.
829 * Returned "facts" (case is not important):
830 * size - size in octets
831 * modify - last modification time
832 * type - entry type (file,dir,OS.unix=block)
833 * (+ cdir and pdir types for MLSD)
834 * unique - unique id of file/directory (inode#)
836 * a: can be appended to (APPE)
837 * d: can be deleted (RMD/DELE)
838 * f: can be renamed (RNFR)
839 * r: can be read (RETR)
840 * w: can be written (STOR)
841 * e: can CWD into this dir
842 * l: this dir can be listed (dir only!)
843 * c: can create files in this dir
844 * m: can create dirs in this dir (MKD)
845 * p: can delete files in this dir
846 * UNIX.mode - unix file mode
849 handle_size_or_mdtm(int need_size)
852 struct tm broken_out;
853 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
854 | sizeof("NNN YYYYMMDDhhmmss\r\n")
858 || stat(G.ftp_arg, &statbuf) != 0
859 || !S_ISREG(statbuf.st_mode)
861 WRITE_ERR(FTP_FILEFAIL);
865 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
867 gmtime_r(&statbuf.st_mtime, &broken_out);
868 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
869 broken_out.tm_year + 1900,
870 broken_out.tm_mon + 1,
876 cmdio_write_raw(buf);
879 /* Upload commands */
881 #if ENABLE_FEATURE_FTPD_WRITE
885 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
886 WRITE_ERR(FTP_FILEFAIL);
889 WRITE_OK(FTP_MKDIROK);
895 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
896 WRITE_ERR(FTP_FILEFAIL);
899 WRITE_OK(FTP_RMDIROK);
905 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
906 WRITE_ERR(FTP_FILEFAIL);
909 WRITE_OK(FTP_DELEOK);
915 free(G.rnfr_filename);
916 G.rnfr_filename = xstrdup(G.ftp_arg);
917 WRITE_OK(FTP_RNFROK);
925 /* If we didn't get a RNFR, throw a wobbly */
926 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
927 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
931 retval = rename(G.rnfr_filename, G.ftp_arg);
932 free(G.rnfr_filename);
933 G.rnfr_filename = NULL;
936 WRITE_ERR(FTP_FILEFAIL);
939 WRITE_OK(FTP_RENAMEOK);
943 handle_upload_common(int is_append, int is_unique)
947 off_t bytes_transferred;
952 offset = G.restart_pos;
955 if (!port_or_pasv_was_seen())
956 return; /* port_or_pasv_was_seen emitted error response */
961 tempname = xstrdup(" FILE: uniq.XXXXXX");
962 local_file_fd = mkstemp(tempname + 7);
963 } else if (G.ftp_arg) {
964 int flags = O_WRONLY | O_CREAT | O_TRUNC;
966 flags = O_WRONLY | O_CREAT | O_APPEND;
968 flags = O_WRONLY | O_CREAT;
969 local_file_fd = open(G.ftp_arg, flags, 0666);
972 if (local_file_fd < 0
973 || fstat(local_file_fd, &statbuf) != 0
974 || !S_ISREG(statbuf.st_mode)
977 WRITE_ERR(FTP_UPLOADFAIL);
978 if (local_file_fd >= 0)
979 goto close_local_and_bail;
982 G.local_file_fd = local_file_fd;
985 xlseek(local_file_fd, offset, SEEK_SET);
987 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
991 goto close_local_and_bail;
993 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
995 if (bytes_transferred < 0)
996 WRITE_ERR(FTP_BADSENDFILE);
998 WRITE_OK(FTP_TRANSFEROK);
1000 close_local_and_bail:
1001 close(local_file_fd);
1002 G.local_file_fd = 0;
1008 handle_upload_common(0, 0);
1015 handle_upload_common(1, 0);
1022 handle_upload_common(0, 1);
1024 #endif /* ENABLE_FEATURE_FTPD_WRITE */
1027 cmdio_get_cmd_and_arg(void)
1037 /* Paranoia. Peer may send 1 gigabyte long cmd... */
1038 /* Using separate len_on_stk instead of len optimizes
1039 * code size (allows len to be in CPU register) */
1040 size_t len_on_stk = 8 * 1024;
1041 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
1047 /* De-escape telnet: 0xff,0xff => 0xff */
1048 /* RFC959 says that ABOR, STAT, QUIT may be sent even during
1049 * data transfer, and may be preceded by telnet's "Interrupt Process"
1050 * code (two-byte sequence 255,244) and then by telnet "Synch" code
1051 * 255,242 (byte 242 is sent with TCP URG bit using send(MSG_OOB)
1052 * and may generate SIGURG on our side. See RFC854).
1053 * So far we don't support that (may install SIGURG handler if we'd want to),
1054 * but we need to at least remove 255,xxx pairs. lftp sends those. */
1055 /* Then de-escape FTP: NUL => '\n' */
1056 /* Testing for \xff:
1057 * Create file named '\xff': echo Hello >`echo -ne "\xff"`
1058 * Try to get it: ftpget -v 127.0.0.1 Eff `echo -ne "\xff\xff"`
1059 * (need "\xff\xff" until ftpget applet is fixed to do escaping :)
1060 * Testing for embedded LF:
1061 * LF_HERE=`echo -ne "LF\nHERE"`
1062 * echo Hello >"$LF_HERE"
1063 * ftpget -v 127.0.0.1 LF_HERE "$LF_HERE"
1068 /* Strip "\r\n" if it is there */
1069 if (len != 0 && cmd[len - 1] == '\n') {
1071 if (len != 0 && cmd[len - 1] == '\r')
1075 src = strchrnul(cmd, 0xff) - cmd;
1076 /* 99,99% there are neither NULs nor 255s and src == len */
1080 if ((unsigned char)(cmd[src]) == 255) {
1082 /* 255,xxx - skip 255 */
1083 if ((unsigned char)(cmd[src]) != 255) {
1084 /* 255,!255 - skip both */
1088 /* 255,255 - retain one 255 */
1091 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1093 } while (src < len);
1101 G.ftp_arg = strchr(cmd, ' ');
1102 if (G.ftp_arg != NULL)
1103 *G.ftp_arg++ = '\0';
1105 /* Uppercase and pack into uint32_t first word of the command */
1108 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1113 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1114 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1116 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1117 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1118 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1119 const_CWD = mk_const3('C', 'W', 'D'),
1120 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1121 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1122 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1123 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1124 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1125 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1126 const_MKD = mk_const3('M', 'K', 'D'),
1127 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1128 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1129 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1130 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1131 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1132 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1133 const_PWD = mk_const3('P', 'W', 'D'),
1134 /* Same as PWD. Reportedly used by windows ftp client */
1135 const_XPWD = mk_const4('X', 'P', 'W', 'D'),
1136 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1137 const_REST = mk_const4('R', 'E', 'S', 'T'),
1138 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1139 const_RMD = mk_const3('R', 'M', 'D'),
1140 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1141 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1142 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1143 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1144 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1145 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1146 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1147 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1148 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1149 const_USER = mk_const4('U', 'S', 'E', 'R'),
1156 OPT_v = (1 << ((!BB_MMU) * 3 + 0)),
1157 OPT_S = (1 << ((!BB_MMU) * 3 + 1)),
1158 OPT_w = (1 << ((!BB_MMU) * 3 + 2)) * ENABLE_FEATURE_FTPD_WRITE,
1161 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1162 int ftpd_main(int argc UNUSED_PARAM, char **argv)
1164 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1165 struct passwd *pw = NULL;
1166 char *anon_opt = NULL;
1168 unsigned abs_timeout;
1174 abs_timeout = 1 * 60 * 60;
1177 opt_complementary = "vv:SS";
1179 opts = getopt32(argv, "vS"
1180 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:"),
1181 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1182 &G.verbose, &verbose_S);
1184 opts = getopt32(argv, "l1AvS"
1185 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:"),
1186 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1187 &G.verbose, &verbose_S);
1188 if (opts & (OPT_l|OPT_1)) {
1189 /* Our secret backdoor to ls */
1192 /* memset(&G, 0, sizeof(G)); - ls_main does it */
1193 return ls_main(/*argc_unused*/ 0, argv);
1196 if (G.verbose < verbose_S)
1197 G.verbose = verbose_S;
1198 if (abs_timeout | G.timeout) {
1199 if (abs_timeout == 0)
1200 abs_timeout = INT_MAX;
1201 G.end_time = monotonic_sec() + abs_timeout;
1202 if (G.timeout > abs_timeout)
1203 G.timeout = abs_timeout;
1205 strcpy(G.msg_ok + 4, MSG_OK );
1206 strcpy(G.msg_err + 4, MSG_ERR);
1208 G.local_addr = get_sock_lsa(STDIN_FILENO);
1209 if (!G.local_addr) {
1210 /* This is confusing:
1211 * bb_error_msg_and_die("stdin is not a socket");
1214 /* Help text says that ftpd must be used as inetd service,
1215 * which is by far the most usual cause of get_sock_lsa
1219 if (!(opts & OPT_v))
1220 logmode = LOGMODE_NONE;
1222 /* LOG_NDELAY is needed since we may chroot later */
1223 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1224 logmode |= LOGMODE_SYSLOG;
1227 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1229 //umask(077); - admin can set umask before starting us
1233 /* We'll always take EPIPE rather than a rude signal, thanks */
1235 /* LIST command spawns chilren. Prevent zombies */
1239 /* Set up options on the command socket (do we need these all? why?) */
1240 setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY);
1241 setsockopt_keepalive(STDIN_FILENO);
1242 /* Telnet protocol over command link may send "urgent" data,
1243 * we prefer it to be received in the "normal" data stream: */
1244 setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE);
1246 WRITE_OK(FTP_GREET);
1247 signal(SIGALRM, timeout_handler);
1249 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1251 uint32_t cmdval = cmdio_get_cmd_and_arg();
1252 if (cmdval == const_USER) {
1253 if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) {
1254 pw = getpwnam(anon_opt);
1256 break; /* does not even ask for password */
1258 pw = getpwnam(G.ftp_arg);
1259 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n");
1260 } else if (cmdval == const_PASS) {
1261 if (check_password(pw, G.ftp_arg) > 0) {
1262 break; /* login success */
1264 cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1266 } else if (cmdval == const_QUIT) {
1267 WRITE_OK(FTP_GOODBYE);
1270 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1273 WRITE_OK(FTP_LOGINOK);
1276 /* Do this after auth, else /etc/passwd is not accessible */
1282 const char *basedir = argv[0];
1284 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1285 close_on_exec_on(G.root_fd);
1287 if (chroot(basedir) == 0)
1296 * If chroot failed, assume that we aren't root,
1297 * and at least chdir to the specified DIR
1298 * (older versions were dying with error message).
1299 * If chroot worked, move current dir to new "/":
1304 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1305 change_identity(pw);
1308 /* RFC-959 Section 5.1
1309 * The following commands and options MUST be supported by every
1310 * server-FTP and user-FTP, except in cases where the underlying
1311 * file system or operating system does not allow or support
1312 * a particular command.
1313 * Type: ASCII Non-print, IMAGE, LOCAL 8
1315 * Structure: File, Record*
1316 * (Record structure is REQUIRED only for hosts whose file
1317 * systems support record structure).
1319 * USER, PASS, ACCT, [bbox: ACCT not supported]
1324 * CWD, CDUP, RMD, MKD, PWD,
1330 * "The argument field is a Telnet string identifying the user's account.
1331 * The command is not necessarily related to the USER command, as some
1332 * sites may require an account for login and others only for specific
1333 * access, such as storing files. In the latter case the command may
1334 * arrive at any time.
1335 * There are reply codes to differentiate these cases for the automation:
1336 * when account information is required for login, the response to
1337 * a successful PASSword command is reply code 332. On the other hand,
1338 * if account information is NOT required for login, the reply to
1339 * a successful PASSword command is 230; and if the account information
1340 * is needed for a command issued later in the dialogue, the server
1341 * should return a 332 or 532 reply depending on whether it stores
1342 * (pending receipt of the ACCounT command) or discards the command,
1347 uint32_t cmdval = cmdio_get_cmd_and_arg();
1349 if (cmdval == const_QUIT) {
1350 WRITE_OK(FTP_GOODBYE);
1353 else if (cmdval == const_USER)
1354 /* This would mean "ok, now give me PASS". */
1355 /*WRITE_OK(FTP_GIVEPWORD);*/
1356 /* vsftpd can be configured to not require that,
1357 * and this also saves one roundtrip:
1359 WRITE_OK(FTP_LOGINOK);
1360 else if (cmdval == const_PASS)
1361 WRITE_OK(FTP_LOGINOK);
1362 else if (cmdval == const_NOOP)
1363 WRITE_OK(FTP_NOOPOK);
1364 else if (cmdval == const_TYPE)
1365 WRITE_OK(FTP_TYPEOK);
1366 else if (cmdval == const_STRU)
1367 WRITE_OK(FTP_STRUOK);
1368 else if (cmdval == const_MODE)
1369 WRITE_OK(FTP_MODEOK);
1370 else if (cmdval == const_ALLO)
1371 WRITE_OK(FTP_ALLOOK);
1372 else if (cmdval == const_SYST)
1373 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1374 else if (cmdval == const_PWD || cmdval == const_XPWD)
1376 else if (cmdval == const_CWD)
1378 else if (cmdval == const_CDUP) /* cd .. */
1380 /* HELP is nearly useless, but we can reuse FEAT for it */
1381 /* lftp uses FEAT */
1382 else if (cmdval == const_HELP || cmdval == const_FEAT)
1383 handle_feat(cmdval == const_HELP
1384 ? STRNUM32(FTP_HELP)
1385 : STRNUM32(FTP_STATOK)
1387 else if (cmdval == const_LIST) /* ls -l */
1389 else if (cmdval == const_NLST) /* "name list", bare ls */
1391 /* SIZE is crucial for wget's download indicator etc */
1392 /* Mozilla, lftp use MDTM (presumably for caching) */
1393 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1394 handle_size_or_mdtm(cmdval == const_SIZE);
1395 else if (cmdval == const_STAT) {
1396 if (G.ftp_arg == NULL)
1401 else if (cmdval == const_PASV)
1403 else if (cmdval == const_EPSV)
1405 else if (cmdval == const_RETR)
1407 else if (cmdval == const_PORT)
1409 else if (cmdval == const_REST)
1411 #if ENABLE_FEATURE_FTPD_WRITE
1412 else if (opts & OPT_w) {
1413 if (cmdval == const_STOR)
1415 else if (cmdval == const_MKD)
1417 else if (cmdval == const_RMD)
1419 else if (cmdval == const_DELE)
1421 else if (cmdval == const_RNFR) /* "rename from" */
1423 else if (cmdval == const_RNTO) /* "rename to" */
1425 else if (cmdval == const_APPE)
1427 else if (cmdval == const_STOU) /* "store unique" */
1434 else if (cmdval == const_STOR
1435 || cmdval == const_MKD
1436 || cmdval == const_RMD
1437 || cmdval == const_DELE
1438 || cmdval == const_RNFR
1439 || cmdval == const_RNTO
1440 || cmdval == const_APPE
1441 || cmdval == const_STOU
1443 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1447 /* Which unsupported commands were seen in the wild?
1448 * (doesn't necessarily mean "we must support them")
1449 * foo 1.2.3: XXXX - comment
1451 #if ENABLE_FEATURE_FTPD_WRITE
1454 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");