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.
18 //config: Simple FTP daemon. You have to run it via inetd.
20 //config:config FEATURE_FTPD_WRITE
21 //config: bool "Enable upload commands"
23 //config: depends on FTPD
25 //config: Enable all kinds of FTP upload commands (-w option)
27 //config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST
28 //config: bool "Enable workaround for RFC-violating clients"
30 //config: depends on FTPD
32 //config: Some ftp clients (among them KDE's Konqueror) issue illegal
33 //config: "LIST -l" requests. This option works around such problems.
34 //config: It might prevent you from listing files starting with "-" and
35 //config: it increases the code size by ~40 bytes.
36 //config: Most other ftp servers seem to behave similar to this.
38 //config:config FEATURE_FTPD_AUTHENTICATION
39 //config: bool "Enable authentication"
41 //config: depends on FTPD
43 //config: Enable basic system login as seen in telnet etc.
45 //applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
47 //kbuild:lib-$(CONFIG_FTPD) += ftpd.o
49 //usage:#define ftpd_trivial_usage
50 //usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t N] [-T N] [DIR]"
51 //usage:#define ftpd_full_usage "\n\n"
52 //usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION(
53 //usage: "Anonymous FTP server. Accesses by clients occur under ftpd's UID.\n"
55 //usage: IF_FEATURE_FTPD_AUTHENTICATION(
56 //usage: "FTP server. "
58 //usage: "Chroots to DIR, if this fails (run by non-root), cds to it.\n"
59 //usage: "Should be used as inetd service, inetd.conf line:\n"
60 //usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n"
61 //usage: "Can be run from tcpsvd:\n"
62 //usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve\n"
63 //usage: "\n -w Allow upload"
64 //usage: IF_FEATURE_FTPD_AUTHENTICATION(
65 //usage: "\n -a USER Enable 'anonymous' login and map it to USER"
67 //usage: "\n -v Log errors to stderr. -vv: verbose log"
68 //usage: "\n -S Log errors to syslog. -SS: verbose log"
69 //usage: "\n -t,-T N Idle and absolute timeout"
72 #include "common_bufsiz.h"
74 #include <netinet/tcp.h>
76 #define FTP_DATACONN 150
77 #define FTP_NOOPOK 200
78 #define FTP_TYPEOK 200
79 #define FTP_PORTOK 200
80 #define FTP_STRUOK 200
81 #define FTP_MODEOK 200
82 #define FTP_ALLOOK 202
83 #define FTP_STATOK 211
84 #define FTP_STATFILE_OK 213
86 #define FTP_SYSTOK 215
88 #define FTP_GOODBYE 221
89 #define FTP_TRANSFEROK 226
90 #define FTP_PASVOK 227
91 /*#define FTP_EPRTOK 228*/
92 #define FTP_EPSVOK 229
93 #define FTP_LOGINOK 230
95 #define FTP_RMDIROK 250
96 #define FTP_DELEOK 250
97 #define FTP_RENAMEOK 250
99 #define FTP_MKDIROK 257
100 #define FTP_GIVEPWORD 331
101 #define FTP_RESTOK 350
102 #define FTP_RNFROK 350
103 #define FTP_TIMEOUT 421
104 #define FTP_BADSENDCONN 425
105 #define FTP_BADSENDNET 426
106 #define FTP_BADSENDFILE 451
107 #define FTP_BADCMD 500
108 #define FTP_COMMANDNOTIMPL 502
109 #define FTP_NEEDUSER 503
110 #define FTP_NEEDRNFR 503
111 #define FTP_BADSTRU 504
112 #define FTP_BADMODE 504
113 #define FTP_LOGINERR 530
114 #define FTP_FILEFAIL 550
115 #define FTP_NOPERM 550
116 #define FTP_UPLOADFAIL 553
119 #define STR(s) STR1(s)
121 /* Convert a constant to 3-digit string, packed into uint32_t */
123 /* Shift for Nth decimal digit */
124 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
125 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
126 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
127 /* And for 4th position (space) */
128 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
130 #define STRNUM32(s) (uint32_t)(0 \
131 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
132 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
133 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
135 #define STRNUM32sp(s) (uint32_t)(0 \
137 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
138 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
139 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
142 #define MSG_OK "Operation successful\r\n"
143 #define MSG_ERR "Error\r\n"
154 off_t local_file_pos;
156 len_and_sockaddr *local_addr;
157 len_and_sockaddr *port_addr;
160 #if ENABLE_FEATURE_FTPD_WRITE
163 /* We need these aligned to uint32_t */
164 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
165 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
167 #define G (*(struct globals*)bb_common_bufsiz1)
168 #define INIT_G() do { \
169 setup_common_bufsiz(); \
170 /* Moved to main */ \
171 /*strcpy(G.msg_ok + 4, MSG_OK );*/ \
172 /*strcpy(G.msg_err + 4, MSG_ERR);*/ \
177 escape_text(const char *prepend, const char *str, unsigned escapee)
179 unsigned retlen, remainlen, chunklen;
183 append = (char)escapee;
186 remainlen = strlen(str);
187 retlen = strlen(prepend);
188 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
189 strcpy(ret, prepend);
192 found = strchrnul(str, escapee);
193 chunklen = found - str + 1;
195 /* Copy chunk up to and including escapee (or NUL) to ret */
196 memcpy(ret + retlen, str, chunklen);
199 if (*found == '\0') {
200 /* It wasn't escapee, it was NUL! */
201 ret[retlen - 1] = append; /* replace NUL */
202 ret[retlen] = '\0'; /* add NUL */
205 ret[retlen++] = escapee; /* duplicate escapee */
211 /* Returns strlen as a bonus */
213 replace_char(char *str, char from, char to)
225 verbose_log(const char *str)
227 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
230 /* NB: status_str is char[4] packed into uint32_t */
232 cmdio_write(uint32_t status_str, const char *str)
237 /* FTP uses telnet protocol for command link.
238 * In telnet, 0xff is an escape char, and needs to be escaped: */
239 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
241 /* FTP sends embedded LFs as NULs */
242 len = replace_char(response, '\n', '\0');
244 response[len++] = '\n'; /* tack on trailing '\n' */
245 xwrite(STDOUT_FILENO, response, len);
247 verbose_log(response);
252 cmdio_write_ok(unsigned status)
254 *(uint32_t *) G.msg_ok = status;
255 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
257 verbose_log(G.msg_ok);
259 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
261 /* TODO: output strerr(errno) if errno != 0? */
263 cmdio_write_error(unsigned status)
265 *(uint32_t *) G.msg_err = status;
266 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
268 verbose_log(G.msg_err);
270 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
273 cmdio_write_raw(const char *p_text)
275 xwrite_str(STDOUT_FILENO, p_text);
281 timeout_handler(int sig UNUSED_PARAM)
284 int sv_errno = errno;
286 if ((int)(monotonic_sec() - G.end_time) >= 0)
289 if (!G.local_file_fd)
292 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
293 if (pos == G.local_file_pos)
295 G.local_file_pos = pos;
302 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
303 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
307 /* Simple commands */
312 char *cwd, *response;
314 cwd = xrealloc_getcwd_or_warn(NULL);
318 /* We have to promote each " to "" */
319 response = escape_text(" \"", cwd, ('"' << 8) + '"');
321 cmdio_write(STRNUM32(FTP_PWDOK), response);
328 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
329 WRITE_ERR(FTP_FILEFAIL);
338 G.ftp_arg = (char*)"..";
345 cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
347 STR(FTP_STATOK)" Ok\r\n");
350 /* Examples of HELP and FEAT:
351 # nc -vvv ftp.kernel.org 21
352 ftp.kernel.org (130.239.17.4:21) open
353 220 Welcome to ftp.kernel.org.
366 214-The following commands are recognized.
367 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
368 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
369 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
374 handle_feat(unsigned status)
376 cmdio_write(status, "-Features:");
377 cmdio_write_raw(" EPSV\r\n"
382 cmdio_write(status, " Ok");
385 /* Download commands */
390 return (G.port_addr != NULL);
396 return (G.pasv_listen_fd > STDOUT_FILENO);
400 port_pasv_cleanup(void)
404 if (G.pasv_listen_fd > STDOUT_FILENO)
405 close(G.pasv_listen_fd);
406 G.pasv_listen_fd = -1;
409 /* On error, emits error code to the peer */
411 ftpdataio_get_pasv_fd(void)
415 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
418 WRITE_ERR(FTP_BADSENDCONN);
422 setsockopt_keepalive(remote_fd);
426 /* Clears port/pasv data.
427 * This means we dont waste resources, for example, keeping
428 * PASV listening socket open when it is no longer needed.
429 * On error, emits error code to the peer (or exits).
430 * On success, emits p_status_msg to the peer.
433 get_remote_transfer_fd(const char *p_status_msg)
438 /* On error, emits error code to the peer */
439 remote_fd = ftpdataio_get_pasv_fd();
442 remote_fd = xconnect_stream(G.port_addr);
449 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
453 /* If there were neither PASV nor PORT, emits error code to the peer */
455 port_or_pasv_was_seen(void)
457 if (!pasv_active() && !port_active()) {
458 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
467 bind_for_passive_mode(void)
474 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
475 setsockopt_reuseaddr(fd);
477 set_nport(&G.local_addr->u.sa, 0);
478 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
480 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
482 port = get_nport(&G.local_addr->u.sa);
492 char *addr, *response;
494 port = bind_for_passive_mode();
496 if (G.local_addr->u.sa.sa_family == AF_INET)
497 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
498 else /* seen this in the wild done by other ftp servers: */
499 addr = xstrdup("0.0.0.0");
500 replace_char(addr, '.', ',');
502 response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
503 addr, (int)(port >> 8), (int)(port & 255));
505 cmdio_write_raw(response);
516 port = bind_for_passive_mode();
517 response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
518 cmdio_write_raw(response);
525 unsigned port, port_hi;
527 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
528 socklen_t peer_ipv4_len;
529 struct sockaddr_in peer_ipv4;
530 struct in_addr port_ipv4_sin_addr;
537 /* PORT command format makes sense only over IPv4 */
539 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
540 || G.local_addr->u.sa.sa_family != AF_INET
544 WRITE_ERR(FTP_BADCMD);
548 comma = strrchr(raw, ',');
552 port = bb_strtou(&comma[1], NULL, 10);
553 if (errno || port > 0xff)
556 comma = strrchr(raw, ',');
560 port_hi = bb_strtou(&comma[1], NULL, 10);
561 if (errno || port_hi > 0xff)
563 port |= port_hi << 8;
565 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
566 replace_char(raw, ',', '.');
568 /* We are verifying that PORT's IP matches getpeername().
569 * Otherwise peer can make us open data connections
570 * to other hosts (security problem!)
571 * This code would be too simplistic:
572 * lsa = xdotted2sockaddr(raw, port);
573 * if (lsa == NULL) goto bail;
575 if (!inet_aton(raw, &port_ipv4_sin_addr))
577 peer_ipv4_len = sizeof(peer_ipv4);
578 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
580 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
583 G.port_addr = xdotted2sockaddr(raw, port);
585 G.port_addr = get_peer_lsa(STDIN_FILENO);
586 set_nport(&G.port_addr->u.sa, htons(port));
588 WRITE_OK(FTP_PORTOK);
594 /* When ftp_arg == NULL simply restart from beginning */
595 G.restart_pos = G.ftp_arg ? xatoi_positive(G.ftp_arg) : 0;
596 WRITE_OK(FTP_RESTOK);
603 off_t bytes_transferred;
606 off_t offset = G.restart_pos;
611 if (!port_or_pasv_was_seen())
612 return; /* port_or_pasv_was_seen emitted error response */
614 /* O_NONBLOCK is useful if file happens to be a device node */
615 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
616 if (local_file_fd < 0) {
617 WRITE_ERR(FTP_FILEFAIL);
621 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
622 /* Note - pretend open failed */
623 WRITE_ERR(FTP_FILEFAIL);
626 G.local_file_fd = local_file_fd;
628 /* Now deactive O_NONBLOCK, otherwise we have a problem
629 * on DMAPI filesystems such as XFS DMAPI.
631 ndelay_off(local_file_fd);
633 /* Set the download offset (from REST) if any */
635 xlseek(local_file_fd, offset, SEEK_SET);
637 response = xasprintf(
638 " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
639 G.ftp_arg, statbuf.st_size);
640 remote_fd = get_remote_transfer_fd(response);
645 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
647 if (bytes_transferred < 0)
648 WRITE_ERR(FTP_BADSENDFILE);
650 WRITE_OK(FTP_TRANSFEROK);
653 close(local_file_fd);
660 popen_ls(const char *opt)
663 struct fd_pair outfd;
667 argv[1] = opt; /* "-lA" or "-1A" */
672 /* Improve compatibility with non-RFC conforming FTP clients
673 * which send e.g. "LIST -l", "LIST -la", "LIST -aL".
674 * See https://bugs.kde.org/show_bug.cgi?id=195578 */
675 if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
676 && G.ftp_arg && G.ftp_arg[0] == '-'
678 const char *tmp = strchr(G.ftp_arg, ' ');
679 if (tmp) /* skip the space */
686 /*fflush_all(); - so far we dont use stdio on output */
687 pid = BB_MMU ? xfork() : xvfork();
693 /* NB: close _first_, then move fd! */
695 xmove_fd(outfd.wr, STDOUT_FILENO);
696 /* Opening /dev/null in chroot is hard.
697 * Just making sure STDIN_FILENO is opened
698 * to something harmless. Paranoia,
699 * ls won't read it anyway */
701 dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */
703 /* memset(&G, 0, sizeof(G)); - ls_main does it */
704 exit(ls_main(/*argc_unused*/ 0, (char**) argv));
706 cur_fd = xopen(".", O_RDONLY | O_DIRECTORY);
707 /* On NOMMU, we want to execute a child - copy of ourself
708 * in order to unblock parent after vfork.
709 * In chroot we usually can't re-exec. Thus we escape
710 * out of the chroot back to original root.
712 if (G.root_fd >= 0) {
713 if (fchdir(G.root_fd) != 0 || chroot(".") != 0)
715 /*close(G.root_fd); - close_on_exec_on() took care of this */
717 /* Child expects directory to list on fd #3 */
719 execv(bb_busybox_exec_path, (char**) argv);
735 handle_dir_common(int opts)
741 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
742 return; /* port_or_pasv_was_seen emitted error response */
744 ls_fd = popen_ls((opts & LONG_LISTING) ? "-lA" : "-1A");
745 ls_fp = xfdopen_for_read(ls_fd);
746 /* FIXME: filenames with embedded newlines are mishandled */
748 if (opts & USE_CTRL_CONN) {
749 /* STAT <filename> */
750 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
752 line = xmalloc_fgetline(ls_fp);
755 /* Hack: 0 results in no status at all */
756 /* Note: it's ok that we don't prepend space,
757 * ftp.kernel.org doesn't do that too */
758 cmdio_write(0, line);
761 WRITE_OK(FTP_STATFILE_OK);
763 /* LIST/NLST [<filename>] */
764 int remote_fd = get_remote_transfer_fd(" Directory listing");
765 if (remote_fd >= 0) {
769 line = xmalloc_fgets(ls_fp);
772 /* I've seen clients complaining when they
773 * are fed with ls output with bare '\n'.
774 * Replace trailing "\n\0" with "\r\n".
777 if (len != 0) /* paranoia check */
778 line[len - 1] = '\r';
780 xwrite(remote_fd, line, len + 1);
785 WRITE_OK(FTP_TRANSFEROK);
787 fclose(ls_fp); /* closes ls_fd too */
792 handle_dir_common(LONG_LISTING);
797 /* NLST returns list of names, "\r\n" terminated without regard
798 * to the current binary flag. Names may start with "/",
799 * then they represent full names (we don't produce such names),
800 * otherwise names are relative to current directory.
801 * Embedded "\n" are replaced by NULs. This is safe since names
802 * can never contain NUL.
804 handle_dir_common(0);
807 handle_stat_file(void)
809 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
812 /* This can be extended to handle MLST, as all info is available
813 * in struct stat for that:
815 * 250-Listing file_name
816 * type=file;size=4161;modify=19970214165800; /dir/dir/file_name
819 * MLST [<file or dir name, "." assumed if not given>]
820 * Returned name should be either the same as requested, or fully qualified.
821 * If there was no parameter, return "" or (preferred) fully-qualified name.
822 * Returned "facts" (case is not important):
823 * size - size in octets
824 * modify - last modification time
825 * type - entry type (file,dir,OS.unix=block)
826 * (+ cdir and pdir types for MLSD)
827 * unique - unique id of file/directory (inode#)
829 * a: can be appended to (APPE)
830 * d: can be deleted (RMD/DELE)
831 * f: can be renamed (RNFR)
832 * r: can be read (RETR)
833 * w: can be written (STOR)
834 * e: can CWD into this dir
835 * l: this dir can be listed (dir only!)
836 * c: can create files in this dir
837 * m: can create dirs in this dir (MKD)
838 * p: can delete files in this dir
839 * UNIX.mode - unix file mode
842 handle_size_or_mdtm(int need_size)
845 struct tm broken_out;
846 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
847 | sizeof("NNN YYYYMMDDhhmmss\r\n")
851 || stat(G.ftp_arg, &statbuf) != 0
852 || !S_ISREG(statbuf.st_mode)
854 WRITE_ERR(FTP_FILEFAIL);
858 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
860 gmtime_r(&statbuf.st_mtime, &broken_out);
861 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
862 broken_out.tm_year + 1900,
863 broken_out.tm_mon + 1,
869 cmdio_write_raw(buf);
872 /* Upload commands */
874 #if ENABLE_FEATURE_FTPD_WRITE
878 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
879 WRITE_ERR(FTP_FILEFAIL);
882 WRITE_OK(FTP_MKDIROK);
888 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
889 WRITE_ERR(FTP_FILEFAIL);
892 WRITE_OK(FTP_RMDIROK);
898 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
899 WRITE_ERR(FTP_FILEFAIL);
902 WRITE_OK(FTP_DELEOK);
908 free(G.rnfr_filename);
909 G.rnfr_filename = xstrdup(G.ftp_arg);
910 WRITE_OK(FTP_RNFROK);
918 /* If we didn't get a RNFR, throw a wobbly */
919 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
920 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
924 retval = rename(G.rnfr_filename, G.ftp_arg);
925 free(G.rnfr_filename);
926 G.rnfr_filename = NULL;
929 WRITE_ERR(FTP_FILEFAIL);
932 WRITE_OK(FTP_RENAMEOK);
936 handle_upload_common(int is_append, int is_unique)
940 off_t bytes_transferred;
945 offset = G.restart_pos;
948 if (!port_or_pasv_was_seen())
949 return; /* port_or_pasv_was_seen emitted error response */
954 tempname = xstrdup(" FILE: uniq.XXXXXX");
955 local_file_fd = mkstemp(tempname + 7);
956 } else if (G.ftp_arg) {
957 int flags = O_WRONLY | O_CREAT | O_TRUNC;
959 flags = O_WRONLY | O_CREAT | O_APPEND;
961 flags = O_WRONLY | O_CREAT;
962 local_file_fd = open(G.ftp_arg, flags, 0666);
965 if (local_file_fd < 0
966 || fstat(local_file_fd, &statbuf) != 0
967 || !S_ISREG(statbuf.st_mode)
970 WRITE_ERR(FTP_UPLOADFAIL);
971 if (local_file_fd >= 0)
972 goto close_local_and_bail;
975 G.local_file_fd = local_file_fd;
978 xlseek(local_file_fd, offset, SEEK_SET);
980 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
984 goto close_local_and_bail;
986 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
988 if (bytes_transferred < 0)
989 WRITE_ERR(FTP_BADSENDFILE);
991 WRITE_OK(FTP_TRANSFEROK);
993 close_local_and_bail:
994 close(local_file_fd);
1001 handle_upload_common(0, 0);
1008 handle_upload_common(1, 0);
1015 handle_upload_common(0, 1);
1017 #endif /* ENABLE_FEATURE_FTPD_WRITE */
1020 cmdio_get_cmd_and_arg(void)
1030 /* Paranoia. Peer may send 1 gigabyte long cmd... */
1031 /* Using separate len_on_stk instead of len optimizes
1032 * code size (allows len to be in CPU register) */
1033 size_t len_on_stk = 8 * 1024;
1034 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
1040 /* De-escape telnet: 0xff,0xff => 0xff */
1041 /* RFC959 says that ABOR, STAT, QUIT may be sent even during
1042 * data transfer, and may be preceded by telnet's "Interrupt Process"
1043 * code (two-byte sequence 255,244) and then by telnet "Synch" code
1044 * 255,242 (byte 242 is sent with TCP URG bit using send(MSG_OOB)
1045 * and may generate SIGURG on our side. See RFC854).
1046 * So far we don't support that (may install SIGURG handler if we'd want to),
1047 * but we need to at least remove 255,xxx pairs. lftp sends those. */
1048 /* Then de-escape FTP: NUL => '\n' */
1049 /* Testing for \xff:
1050 * Create file named '\xff': echo Hello >`echo -ne "\xff"`
1051 * Try to get it: ftpget -v 127.0.0.1 Eff `echo -ne "\xff\xff"`
1052 * (need "\xff\xff" until ftpget applet is fixed to do escaping :)
1053 * Testing for embedded LF:
1054 * LF_HERE=`echo -ne "LF\nHERE"`
1055 * echo Hello >"$LF_HERE"
1056 * ftpget -v 127.0.0.1 LF_HERE "$LF_HERE"
1061 /* Strip "\r\n" if it is there */
1062 if (len != 0 && cmd[len - 1] == '\n') {
1064 if (len != 0 && cmd[len - 1] == '\r')
1068 src = strchrnul(cmd, 0xff) - cmd;
1069 /* 99,99% there are neither NULs nor 255s and src == len */
1073 if ((unsigned char)(cmd[src]) == 255) {
1075 /* 255,xxx - skip 255 */
1076 if ((unsigned char)(cmd[src]) != 255) {
1077 /* 255,!255 - skip both */
1081 /* 255,255 - retain one 255 */
1084 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1086 } while (src < len);
1094 G.ftp_arg = strchr(cmd, ' ');
1095 if (G.ftp_arg != NULL)
1096 *G.ftp_arg++ = '\0';
1098 /* Uppercase and pack into uint32_t first word of the command */
1101 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1106 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1107 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1109 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1110 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1111 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1112 const_CWD = mk_const3('C', 'W', 'D'),
1113 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1114 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1115 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1116 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1117 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1118 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1119 const_MKD = mk_const3('M', 'K', 'D'),
1120 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1121 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1122 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1123 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1124 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1125 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1126 const_PWD = mk_const3('P', 'W', 'D'),
1127 /* Same as PWD. Reportedly used by windows ftp client */
1128 const_XPWD = mk_const4('X', 'P', 'W', 'D'),
1129 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1130 const_REST = mk_const4('R', 'E', 'S', 'T'),
1131 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1132 const_RMD = mk_const3('R', 'M', 'D'),
1133 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1134 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1135 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1136 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1137 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1138 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1139 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1140 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1141 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1142 const_USER = mk_const4('U', 'S', 'E', 'R'),
1149 OPT_v = (1 << ((!BB_MMU) * 3 + 0)),
1150 OPT_S = (1 << ((!BB_MMU) * 3 + 1)),
1151 OPT_w = (1 << ((!BB_MMU) * 3 + 2)) * ENABLE_FEATURE_FTPD_WRITE,
1154 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1155 int ftpd_main(int argc UNUSED_PARAM, char **argv)
1157 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1158 struct passwd *pw = NULL;
1159 char *anon_opt = NULL;
1161 unsigned abs_timeout;
1167 abs_timeout = 1 * 60 * 60;
1170 opt_complementary = "vv:SS";
1172 opts = getopt32(argv, "vS"
1173 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:"),
1174 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1175 &G.verbose, &verbose_S);
1177 opts = getopt32(argv, "l1AvS"
1178 IF_FEATURE_FTPD_WRITE("w") "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:"),
1179 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1180 &G.verbose, &verbose_S);
1181 if (opts & (OPT_l|OPT_1)) {
1182 /* Our secret backdoor to ls */
1185 /* memset(&G, 0, sizeof(G)); - ls_main does it */
1186 return ls_main(/*argc_unused*/ 0, argv);
1189 if (G.verbose < verbose_S)
1190 G.verbose = verbose_S;
1191 if (abs_timeout | G.timeout) {
1192 if (abs_timeout == 0)
1193 abs_timeout = INT_MAX;
1194 G.end_time = monotonic_sec() + abs_timeout;
1195 if (G.timeout > abs_timeout)
1196 G.timeout = abs_timeout;
1198 strcpy(G.msg_ok + 4, MSG_OK );
1199 strcpy(G.msg_err + 4, MSG_ERR);
1201 G.local_addr = get_sock_lsa(STDIN_FILENO);
1202 if (!G.local_addr) {
1203 /* This is confusing:
1204 * bb_error_msg_and_die("stdin is not a socket");
1207 /* Help text says that ftpd must be used as inetd service,
1208 * which is by far the most usual cause of get_sock_lsa
1212 if (!(opts & OPT_v))
1213 logmode = LOGMODE_NONE;
1215 /* LOG_NDELAY is needed since we may chroot later */
1216 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1217 logmode |= LOGMODE_SYSLOG;
1220 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1222 //umask(077); - admin can set umask before starting us
1226 /* We'll always take EPIPE rather than a rude signal, thanks */
1228 /* LIST command spawns chilren. Prevent zombies */
1232 /* Set up options on the command socket (do we need these all? why?) */
1233 setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY);
1234 setsockopt_keepalive(STDIN_FILENO);
1235 /* Telnet protocol over command link may send "urgent" data,
1236 * we prefer it to be received in the "normal" data stream: */
1237 setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE);
1239 WRITE_OK(FTP_GREET);
1240 signal(SIGALRM, timeout_handler);
1242 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1244 uint32_t cmdval = cmdio_get_cmd_and_arg();
1245 if (cmdval == const_USER) {
1246 if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) {
1247 pw = getpwnam(anon_opt);
1249 break; /* does not even ask for password */
1251 pw = getpwnam(G.ftp_arg);
1252 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n");
1253 } else if (cmdval == const_PASS) {
1254 if (check_password(pw, G.ftp_arg) > 0) {
1255 break; /* login success */
1257 cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1259 } else if (cmdval == const_QUIT) {
1260 WRITE_OK(FTP_GOODBYE);
1263 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1266 WRITE_OK(FTP_LOGINOK);
1269 /* Do this after auth, else /etc/passwd is not accessible */
1275 const char *basedir = argv[0];
1277 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1278 close_on_exec_on(G.root_fd);
1280 if (chroot(basedir) == 0)
1289 * If chroot failed, assume that we aren't root,
1290 * and at least chdir to the specified DIR
1291 * (older versions were dying with error message).
1292 * If chroot worked, move current dir to new "/":
1297 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1298 change_identity(pw);
1301 /* RFC-959 Section 5.1
1302 * The following commands and options MUST be supported by every
1303 * server-FTP and user-FTP, except in cases where the underlying
1304 * file system or operating system does not allow or support
1305 * a particular command.
1306 * Type: ASCII Non-print, IMAGE, LOCAL 8
1308 * Structure: File, Record*
1309 * (Record structure is REQUIRED only for hosts whose file
1310 * systems support record structure).
1312 * USER, PASS, ACCT, [bbox: ACCT not supported]
1317 * CWD, CDUP, RMD, MKD, PWD,
1323 * "The argument field is a Telnet string identifying the user's account.
1324 * The command is not necessarily related to the USER command, as some
1325 * sites may require an account for login and others only for specific
1326 * access, such as storing files. In the latter case the command may
1327 * arrive at any time.
1328 * There are reply codes to differentiate these cases for the automation:
1329 * when account information is required for login, the response to
1330 * a successful PASSword command is reply code 332. On the other hand,
1331 * if account information is NOT required for login, the reply to
1332 * a successful PASSword command is 230; and if the account information
1333 * is needed for a command issued later in the dialogue, the server
1334 * should return a 332 or 532 reply depending on whether it stores
1335 * (pending receipt of the ACCounT command) or discards the command,
1340 uint32_t cmdval = cmdio_get_cmd_and_arg();
1342 if (cmdval == const_QUIT) {
1343 WRITE_OK(FTP_GOODBYE);
1346 else if (cmdval == const_USER)
1347 /* This would mean "ok, now give me PASS". */
1348 /*WRITE_OK(FTP_GIVEPWORD);*/
1349 /* vsftpd can be configured to not require that,
1350 * and this also saves one roundtrip:
1352 WRITE_OK(FTP_LOGINOK);
1353 else if (cmdval == const_PASS)
1354 WRITE_OK(FTP_LOGINOK);
1355 else if (cmdval == const_NOOP)
1356 WRITE_OK(FTP_NOOPOK);
1357 else if (cmdval == const_TYPE)
1358 WRITE_OK(FTP_TYPEOK);
1359 else if (cmdval == const_STRU)
1360 WRITE_OK(FTP_STRUOK);
1361 else if (cmdval == const_MODE)
1362 WRITE_OK(FTP_MODEOK);
1363 else if (cmdval == const_ALLO)
1364 WRITE_OK(FTP_ALLOOK);
1365 else if (cmdval == const_SYST)
1366 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1367 else if (cmdval == const_PWD || cmdval == const_XPWD)
1369 else if (cmdval == const_CWD)
1371 else if (cmdval == const_CDUP) /* cd .. */
1373 /* HELP is nearly useless, but we can reuse FEAT for it */
1374 /* lftp uses FEAT */
1375 else if (cmdval == const_HELP || cmdval == const_FEAT)
1376 handle_feat(cmdval == const_HELP
1377 ? STRNUM32(FTP_HELP)
1378 : STRNUM32(FTP_STATOK)
1380 else if (cmdval == const_LIST) /* ls -l */
1382 else if (cmdval == const_NLST) /* "name list", bare ls */
1384 /* SIZE is crucial for wget's download indicator etc */
1385 /* Mozilla, lftp use MDTM (presumably for caching) */
1386 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1387 handle_size_or_mdtm(cmdval == const_SIZE);
1388 else if (cmdval == const_STAT) {
1389 if (G.ftp_arg == NULL)
1394 else if (cmdval == const_PASV)
1396 else if (cmdval == const_EPSV)
1398 else if (cmdval == const_RETR)
1400 else if (cmdval == const_PORT)
1402 else if (cmdval == const_REST)
1404 #if ENABLE_FEATURE_FTPD_WRITE
1405 else if (opts & OPT_w) {
1406 if (cmdval == const_STOR)
1408 else if (cmdval == const_MKD)
1410 else if (cmdval == const_RMD)
1412 else if (cmdval == const_DELE)
1414 else if (cmdval == const_RNFR) /* "rename from" */
1416 else if (cmdval == const_RNTO) /* "rename to" */
1418 else if (cmdval == const_APPE)
1420 else if (cmdval == const_STOU) /* "store unique" */
1427 else if (cmdval == const_STOR
1428 || cmdval == const_MKD
1429 || cmdval == const_RMD
1430 || cmdval == const_DELE
1431 || cmdval == const_RNFR
1432 || cmdval == const_RNTO
1433 || cmdval == const_APPE
1434 || cmdval == const_STOU
1436 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1440 /* Which unsupported commands were seen in the wild?
1441 * (doesn't necessarily mean "we must support them")
1442 * foo 1.2.3: XXXX - comment
1444 #if ENABLE_FEATURE_FTPD_WRITE
1447 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");