1 /* vi: set sw=4 ts=4: */
2 /*
3  * Simple FTP daemon, based on vsftpd 2.0.7 (written by Chris Evans)
4  *
5  * Author: Adam Tkac <vonsch@gmail.com>
6  *
7  * Licensed under GPLv2, see file LICENSE in this source tree.
8  *
9  * Only subset of FTP protocol is implemented but vast majority of clients
10  * should not have any problem.
11  *
12  * You have to run this daemon via inetd.
13  */
14 //config:config FTPD
15 //config:	bool "ftpd (30 kb)"
16 //config:	default y
17 //config:	help
18 //config:	Simple FTP daemon. You have to run it via inetd.
19 //config:
20 //config:config FEATURE_FTPD_WRITE
21 //config:	bool "Enable -w (upload commands)"
22 //config:	default y
23 //config:	depends on FTPD
24 //config:	help
25 //config:	Enable -w option. "ftpd -w" will accept upload commands
26 //config:	such as STOR, STOU, APPE, DELE, MKD, RMD, rename commands.
27 //config:
28 //config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST
29 //config:	bool "Enable workaround for RFC-violating clients"
30 //config:	default y
31 //config:	depends on FTPD
32 //config:	help
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.
38 //config:
39 //config:config FEATURE_FTPD_AUTHENTICATION
40 //config:	bool "Enable authentication"
41 //config:	default y
42 //config:	depends on FTPD
43 //config:	help
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).
47 //config:
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.
51 
52 //applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
53 
54 //kbuild:lib-$(CONFIG_FTPD) += ftpd.o
55 
56 //usage:#define ftpd_trivial_usage
57 //usage:       "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t SEC] [-T SEC] [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"
61 //usage:	)
62 //usage:	IF_FEATURE_FTPD_AUTHENTICATION(
63 //usage:       "FTP server. "
64 //usage:	)
65 //usage:       "Chroots to DIR, if this fails (run by non-root), cds to it.\n"
66 //usage:       "It is an 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"
70 //usage:     "\n"
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 //
75 // if !FTPD_AUTHENTICATION, -A is accepted too, but not shown in --help
76 // since it's the only supported mode in that configuration
77 //
78 //usage:     "\n	-a USER	Enable 'anonymous' login and map it to USER"
79 //usage:	)
80 //usage:     "\n	-v	Log errors to stderr. -vv: verbose log"
81 //usage:     "\n	-S	Log errors to syslog. -SS: verbose log"
82 //usage:     "\n	-t,-T N	Idle and absolute timeout"
83 
84 #include "libbb.h"
85 #include "common_bufsiz.h"
86 #include <syslog.h>
87 #include <netinet/tcp.h>
88 
89 #define FTP_DATACONN            150
90 #define FTP_NOOPOK              200
91 #define FTP_TYPEOK              200
92 #define FTP_PORTOK              200
93 #define FTP_STRUOK              200
94 #define FTP_MODEOK              200
95 #define FTP_ALLOOK              202
96 #define FTP_STATOK              211
97 #define FTP_STATFILE_OK         213
98 #define FTP_HELP                214
99 #define FTP_SYSTOK              215
100 #define FTP_GREET               220
101 #define FTP_GOODBYE             221
102 #define FTP_TRANSFEROK          226
103 #define FTP_PASVOK              227
104 /*#define FTP_EPRTOK              228*/
105 #define FTP_EPSVOK              229
106 #define FTP_LOGINOK             230
107 #define FTP_CWDOK               250
108 #define FTP_RMDIROK             250
109 #define FTP_DELEOK              250
110 #define FTP_RENAMEOK            250
111 #define FTP_PWDOK               257
112 #define FTP_MKDIROK             257
113 #define FTP_GIVEPWORD           331
114 #define FTP_RESTOK              350
115 #define FTP_RNFROK              350
116 #define FTP_TIMEOUT             421
117 #define FTP_BADSENDCONN         425
118 #define FTP_BADSENDNET          426
119 #define FTP_BADSENDFILE         451
120 #define FTP_BADCMD              500
121 #define FTP_COMMANDNOTIMPL      502
122 #define FTP_NEEDUSER            503
123 #define FTP_NEEDRNFR            503
124 #define FTP_BADSTRU             504
125 #define FTP_BADMODE             504
126 #define FTP_LOGINERR            530
127 #define FTP_FILEFAIL            550
128 #define FTP_NOPERM              550
129 #define FTP_UPLOADFAIL          553
130 
131 #define STR1(s) #s
132 #define STR(s) STR1(s)
133 
134 /* Convert a constant to 3-digit string, packed into uint32_t */
135 enum {
136 	/* Shift for Nth decimal digit */
137 	SHIFT2  =  0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
138 	SHIFT1  =  8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
139 	SHIFT0  = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
140 	/* And for 4th position (space) */
141 	SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
142 };
143 #define STRNUM32(s) (uint32_t)(0 \
144 	| (('0' + ((s) / 1 % 10)) << SHIFT0) \
145 	| (('0' + ((s) / 10 % 10)) << SHIFT1) \
146 	| (('0' + ((s) / 100 % 10)) << SHIFT2) \
147 )
148 #define STRNUM32sp(s) (uint32_t)(0 \
149 	| (' ' << SHIFTsp) \
150 	| (('0' + ((s) / 1 % 10)) << SHIFT0) \
151 	| (('0' + ((s) / 10 % 10)) << SHIFT1) \
152 	| (('0' + ((s) / 100 % 10)) << SHIFT2) \
153 )
154 
155 #define MSG_OK "Operation successful\r\n"
156 #define MSG_ERR "Error\r\n"
157 
158 struct globals {
159 	int pasv_listen_fd;
160 #if !BB_MMU
161 	int root_fd;
162 #endif
163 	int local_file_fd;
164 	unsigned end_time;
165 	unsigned timeout;
166 	unsigned verbose;
167 	off_t local_file_pos;
168 	off_t restart_pos;
169 	len_and_sockaddr *local_addr;
170 	len_and_sockaddr *port_addr;
171 	char *ftp_cmd;
172 	char *ftp_arg;
173 #if ENABLE_FEATURE_FTPD_WRITE
174 	char *rnfr_filename;
175 #endif
176 	/* We need these aligned to uint32_t */
177 	char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
178 	char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
179 } FIX_ALIASING;
180 #define G (*ptr_to_globals)
181 /* ^^^ about 75 bytes smaller code than this: */
182 //#define G (*(struct globals*)bb_common_bufsiz1)
183 #define INIT_G() do { \
184 	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
185 	/*setup_common_bufsiz();*/ \
186 	\
187 	/* Moved to main */ \
188 	/*strcpy(G.msg_ok  + 4, MSG_OK );*/ \
189 	/*strcpy(G.msg_err + 4, MSG_ERR);*/ \
190 } while (0)
191 
192 
193 static char *
escape_text(const char * prepend,const char * str,unsigned escapee)194 escape_text(const char *prepend, const char *str, unsigned escapee)
195 {
196 	unsigned retlen, remainlen, chunklen;
197 	char *ret, *found;
198 	char append;
199 
200 	append = (char)escapee;
201 	escapee >>= 8;
202 
203 	remainlen = strlen(str);
204 	retlen = strlen(prepend);
205 	ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
206 	strcpy(ret, prepend);
207 
208 	for (;;) {
209 		found = strchrnul(str, escapee);
210 		chunklen = found - str + 1;
211 
212 		/* Copy chunk up to and including escapee (or NUL) to ret */
213 		memcpy(ret + retlen, str, chunklen);
214 		retlen += chunklen;
215 
216 		if (*found == '\0') {
217 			/* It wasn't escapee, it was NUL! */
218 			ret[retlen - 1] = append; /* replace NUL */
219 			ret[retlen] = '\0'; /* add NUL */
220 			break;
221 		}
222 		ret[retlen++] = escapee; /* duplicate escapee */
223 		str = found + 1;
224 	}
225 	return ret;
226 }
227 
228 /* Returns strlen as a bonus */
229 static unsigned
replace_char(char * str,char from,char to)230 replace_char(char *str, char from, char to)
231 {
232 	char *p = str;
233 	while (*p) {
234 		if (*p == from)
235 			*p = to;
236 		p++;
237 	}
238 	return p - str;
239 }
240 
241 static void
verbose_log(const char * str)242 verbose_log(const char *str)
243 {
244 	bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
245 }
246 
247 /* NB: status_str is char[4] packed into uint32_t */
248 static void
cmdio_write(uint32_t status_str,const char * str)249 cmdio_write(uint32_t status_str, const char *str)
250 {
251 	char *response;
252 	int len;
253 
254 	/* FTP uses telnet protocol for command link.
255 	 * In telnet, 0xff is an escape char, and needs to be escaped: */
256 	response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
257 
258 	/* FTP sends embedded LFs as NULs */
259 	len = replace_char(response, '\n', '\0');
260 
261 	response[len++] = '\n'; /* tack on trailing '\n' */
262 	xwrite(STDOUT_FILENO, response, len);
263 	if (G.verbose > 1)
264 		verbose_log(response);
265 	free(response);
266 }
267 
268 static void
cmdio_write_ok(unsigned status)269 cmdio_write_ok(unsigned status)
270 {
271 	*(bb__aliased_uint32_t *) G.msg_ok = status;
272 	xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
273 	if (G.verbose > 1)
274 		verbose_log(G.msg_ok);
275 }
276 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
277 
278 /* TODO: output strerr(errno) if errno != 0? */
279 static void
cmdio_write_error(unsigned status)280 cmdio_write_error(unsigned status)
281 {
282 	*(bb__aliased_uint32_t *) G.msg_err = status;
283 	xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
284 	if (G.verbose > 0)
285 		verbose_log(G.msg_err);
286 }
287 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
288 
289 static void
cmdio_write_raw(const char * p_text)290 cmdio_write_raw(const char *p_text)
291 {
292 	xwrite_str(STDOUT_FILENO, p_text);
293 	if (G.verbose > 1)
294 		verbose_log(p_text);
295 }
296 
297 static void
timeout_handler(int sig UNUSED_PARAM)298 timeout_handler(int sig UNUSED_PARAM)
299 {
300 	off_t pos;
301 	int sv_errno = errno;
302 
303 	if ((int)(monotonic_sec() - G.end_time) >= 0)
304 		goto timed_out;
305 
306 	if (!G.local_file_fd)
307 		goto timed_out;
308 
309 	pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
310 	if (pos == G.local_file_pos)
311 		goto timed_out;
312 	G.local_file_pos = pos;
313 
314 	alarm(G.timeout);
315 	errno = sv_errno;
316 	return;
317 
318  timed_out:
319 	cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
320 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
321 	exit(1);
322 }
323 
324 /* Simple commands */
325 
326 static void
handle_pwd(void)327 handle_pwd(void)
328 {
329 	char *cwd, *response;
330 
331 	cwd = xrealloc_getcwd_or_warn(NULL);
332 	if (cwd == NULL)
333 		cwd = xstrdup("");
334 
335 	/* We have to promote each " to "" */
336 	response = escape_text(" \"", cwd, ('"' << 8) + '"');
337 	free(cwd);
338 	cmdio_write(STRNUM32(FTP_PWDOK), response);
339 	free(response);
340 }
341 
342 static void
handle_cwd(void)343 handle_cwd(void)
344 {
345 	if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
346 		WRITE_ERR(FTP_FILEFAIL);
347 		return;
348 	}
349 	WRITE_OK(FTP_CWDOK);
350 }
351 
352 static void
handle_cdup(void)353 handle_cdup(void)
354 {
355 	G.ftp_arg = (char*)"..";
356 	handle_cwd();
357 }
358 
359 static void
handle_stat(void)360 handle_stat(void)
361 {
362 	cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
363 			" TYPE: BINARY\r\n"
364 			STR(FTP_STATOK)" Ok\r\n");
365 }
366 
367 /* Examples of HELP and FEAT:
368 # nc -vvv ftp.kernel.org 21
369 ftp.kernel.org (130.239.17.4:21) open
370 220 Welcome to ftp.kernel.org.
371 FEAT
372 211-Features:
373  EPRT
374  EPSV
375  MDTM
376  PASV
377  REST STREAM
378  SIZE
379  TVFS
380  UTF8
381 211 End
382 HELP
383 214-The following commands are recognized.
384  ABOR ACCT ALLO APPE CDUP CWD  DELE EPRT EPSV FEAT HELP LIST MDTM MKD
385  MODE NLST NOOP OPTS PASS PASV PORT PWD  QUIT REIN REST RETR RMD  RNFR
386  RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
387  XPWD XRMD
388 214 Help OK.
389 */
390 static void
handle_feat(unsigned status)391 handle_feat(unsigned status)
392 {
393 	cmdio_write(status, "-Features:");
394 	cmdio_write_raw(" EPSV\r\n"
395 			" PASV\r\n"
396 			" REST STREAM\r\n"
397 			" MDTM\r\n"
398 			" SIZE\r\n");
399 	cmdio_write(status, " Ok");
400 }
401 
402 /* Download commands */
403 
404 static inline int
port_active(void)405 port_active(void)
406 {
407 	return (G.port_addr != NULL);
408 }
409 
410 static inline int
pasv_active(void)411 pasv_active(void)
412 {
413 	return (G.pasv_listen_fd > STDOUT_FILENO);
414 }
415 
416 static void
port_pasv_cleanup(void)417 port_pasv_cleanup(void)
418 {
419 	free(G.port_addr);
420 	G.port_addr = NULL;
421 	if (G.pasv_listen_fd > STDOUT_FILENO)
422 		close(G.pasv_listen_fd);
423 	G.pasv_listen_fd = -1;
424 }
425 
426 /* On error, emits error code to the peer */
427 static int
ftpdataio_get_pasv_fd(void)428 ftpdataio_get_pasv_fd(void)
429 {
430 	int remote_fd;
431 
432 	remote_fd = accept(G.pasv_listen_fd, NULL, 0);
433 
434 	if (remote_fd < 0) {
435 		WRITE_ERR(FTP_BADSENDCONN);
436 		return remote_fd;
437 	}
438 
439 	setsockopt_keepalive(remote_fd);
440 	return remote_fd;
441 }
442 
443 /* Clears port/pasv data.
444  * This means we dont waste resources, for example, keeping
445  * PASV listening socket open when it is no longer needed.
446  * On error, emits error code to the peer (or exits).
447  * On success, emits p_status_msg to the peer.
448  */
449 static int
get_remote_transfer_fd(const char * p_status_msg)450 get_remote_transfer_fd(const char *p_status_msg)
451 {
452 	int remote_fd;
453 
454 	if (pasv_active())
455 		/* On error, emits error code to the peer */
456 		remote_fd = ftpdataio_get_pasv_fd();
457 	else
458 		/* Exits on error */
459 		remote_fd = xconnect_stream(G.port_addr);
460 
461 	port_pasv_cleanup();
462 
463 	if (remote_fd < 0)
464 		return remote_fd;
465 
466 	cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
467 	return remote_fd;
468 }
469 
470 /* If there were neither PASV nor PORT, emits error code to the peer */
471 static int
port_or_pasv_was_seen(void)472 port_or_pasv_was_seen(void)
473 {
474 	if (!pasv_active() && !port_active()) {
475 		cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
476 		return 0;
477 	}
478 
479 	return 1;
480 }
481 
482 /* Exits on error */
483 static unsigned
bind_for_passive_mode(void)484 bind_for_passive_mode(void)
485 {
486 	int fd;
487 	unsigned port;
488 
489 	port_pasv_cleanup();
490 
491 	G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
492 	setsockopt_reuseaddr(fd);
493 
494 	set_nport(&G.local_addr->u.sa, 0);
495 	xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
496 	xlisten(fd, 1);
497 	getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
498 
499 	port = get_nport(&G.local_addr->u.sa);
500 	port = ntohs(port);
501 	return port;
502 }
503 
504 /* Exits on error */
505 static void
handle_pasv(void)506 handle_pasv(void)
507 {
508 	unsigned port;
509 	char *addr, *response;
510 
511 	port = bind_for_passive_mode();
512 
513 	if (G.local_addr->u.sa.sa_family == AF_INET)
514 		addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
515 	else /* seen this in the wild done by other ftp servers: */
516 		addr = xstrdup("0.0.0.0");
517 	replace_char(addr, '.', ',');
518 
519 	response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
520 			addr, (int)(port >> 8), (int)(port & 255));
521 	free(addr);
522 	cmdio_write_raw(response);
523 	free(response);
524 }
525 
526 /* Exits on error */
527 static void
handle_epsv(void)528 handle_epsv(void)
529 {
530 	unsigned port;
531 	char *response;
532 
533 	port = bind_for_passive_mode();
534 	response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
535 	cmdio_write_raw(response);
536 	free(response);
537 }
538 
539 static void
handle_port(void)540 handle_port(void)
541 {
542 	unsigned port, port_hi;
543 	char *raw, *comma;
544 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
545 	socklen_t peer_ipv4_len;
546 	struct sockaddr_in peer_ipv4;
547 	struct in_addr port_ipv4_sin_addr;
548 #endif
549 
550 	port_pasv_cleanup();
551 
552 	raw = G.ftp_arg;
553 
554 	/* PORT command format makes sense only over IPv4 */
555 	if (!raw
556 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
557 	 || G.local_addr->u.sa.sa_family != AF_INET
558 #endif
559 	) {
560  bail:
561 		WRITE_ERR(FTP_BADCMD);
562 		return;
563 	}
564 
565 	comma = strrchr(raw, ',');
566 	if (comma == NULL)
567 		goto bail;
568 	*comma = '\0';
569 	port = bb_strtou(&comma[1], NULL, 10);
570 	if (errno || port > 0xff)
571 		goto bail;
572 
573 	comma = strrchr(raw, ',');
574 	if (comma == NULL)
575 		goto bail;
576 	*comma = '\0';
577 	port_hi = bb_strtou(&comma[1], NULL, 10);
578 	if (errno || port_hi > 0xff)
579 		goto bail;
580 	port |= port_hi << 8;
581 
582 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
583 	replace_char(raw, ',', '.');
584 
585 	/* We are verifying that PORT's IP matches getpeername().
586 	 * Otherwise peer can make us open data connections
587 	 * to other hosts (security problem!)
588 	 * This code would be too simplistic:
589 	 * lsa = xdotted2sockaddr(raw, port);
590 	 * if (lsa == NULL) goto bail;
591 	 */
592 	if (!inet_aton(raw, &port_ipv4_sin_addr))
593 		goto bail;
594 	peer_ipv4_len = sizeof(peer_ipv4);
595 	if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
596 		goto bail;
597 	if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
598 		goto bail;
599 
600 	G.port_addr = xdotted2sockaddr(raw, port);
601 #else
602 	G.port_addr = get_peer_lsa(STDIN_FILENO);
603 	set_nport(&G.port_addr->u.sa, htons(port));
604 #endif
605 	WRITE_OK(FTP_PORTOK);
606 }
607 
608 static void
handle_rest(void)609 handle_rest(void)
610 {
611 	/* When ftp_arg == NULL simply restart from beginning */
612 	G.restart_pos = G.ftp_arg ? XATOOFF(G.ftp_arg) : 0;
613 	WRITE_OK(FTP_RESTOK);
614 }
615 
616 static void
handle_retr(void)617 handle_retr(void)
618 {
619 	struct stat statbuf;
620 	off_t bytes_transferred;
621 	int remote_fd;
622 	int local_file_fd;
623 	off_t offset = G.restart_pos;
624 	char *response;
625 
626 	G.restart_pos = 0;
627 
628 	if (!port_or_pasv_was_seen())
629 		return; /* port_or_pasv_was_seen emitted error response */
630 
631 	/* O_NONBLOCK is useful if file happens to be a device node */
632 	local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
633 	if (local_file_fd < 0) {
634 		WRITE_ERR(FTP_FILEFAIL);
635 		return;
636 	}
637 
638 	if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
639 		/* Note - pretend open failed */
640 		WRITE_ERR(FTP_FILEFAIL);
641 		goto file_close_out;
642 	}
643 	G.local_file_fd = local_file_fd;
644 
645 	/* Now deactive O_NONBLOCK, otherwise we have a problem
646 	 * on DMAPI filesystems such as XFS DMAPI.
647 	 */
648 	ndelay_off(local_file_fd);
649 
650 	/* Set the download offset (from REST) if any */
651 	if (offset != 0)
652 		xlseek(local_file_fd, offset, SEEK_SET);
653 
654 	response = xasprintf(
655 		" Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
656 		G.ftp_arg, statbuf.st_size);
657 	remote_fd = get_remote_transfer_fd(response);
658 	free(response);
659 	if (remote_fd < 0)
660 		goto file_close_out;
661 
662 	bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
663 	close(remote_fd);
664 	if (bytes_transferred < 0)
665 		WRITE_ERR(FTP_BADSENDFILE);
666 	else
667 		WRITE_OK(FTP_TRANSFEROK);
668 
669  file_close_out:
670 	close(local_file_fd);
671 	G.local_file_fd = 0;
672 }
673 
674 /* List commands */
675 
676 static int
popen_ls(const char * opt)677 popen_ls(const char *opt)
678 {
679 	const char *argv[5];
680 	struct fd_pair outfd;
681 	pid_t pid;
682 
683 	argv[0] = "ftpd";
684 	argv[1] = opt; /* "-lA" or "-1A" */
685 	argv[2] = "--";
686 	argv[3] = G.ftp_arg;
687 	argv[4] = NULL;
688 
689 	/* Improve compatibility with non-RFC conforming FTP clients
690 	 * which send e.g. "LIST -l", "LIST -la", "LIST -aL".
691 	 * See https://bugs.kde.org/show_bug.cgi?id=195578 */
692 	if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
693 	 && G.ftp_arg && G.ftp_arg[0] == '-'
694 	) {
695 		const char *tmp = strchr(G.ftp_arg, ' ');
696 		if (tmp) /* skip the space */
697 			tmp++;
698 		argv[3] = tmp;
699 	}
700 
701 	xpiped_pair(outfd);
702 
703 	/*fflush_all(); - so far we dont use stdio on output */
704 	pid = BB_MMU ? xfork() : xvfork();
705 	if (pid == 0) {
706 #if !BB_MMU
707 		int cur_fd;
708 #endif
709 		/* child */
710 		/* NB: close _first_, then move fd! */
711 		close(outfd.rd);
712 		xmove_fd(outfd.wr, STDOUT_FILENO);
713 		/* Opening /dev/null in chroot is hard.
714 		 * Just making sure STDIN_FILENO is opened
715 		 * to something harmless. Paranoia,
716 		 * ls won't read it anyway */
717 		close(STDIN_FILENO);
718 		dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */
719 #if BB_MMU
720 		/* memset(&G, 0, sizeof(G)); - ls_main does it */
721 		exit(ls_main(/*argc_unused*/ 0, (char**) argv));
722 #else
723 		cur_fd = xopen(".", O_RDONLY | O_DIRECTORY);
724 		/* On NOMMU, we want to execute a child - copy of ourself
725 		 * in order to unblock parent after vfork.
726 		 * In chroot we usually can't re-exec. Thus we escape
727 		 * out of the chroot back to original root.
728 		 */
729 		if (G.root_fd >= 0) {
730 			if (fchdir(G.root_fd) != 0 || chroot(".") != 0)
731 				_exit(127);
732 			/*close(G.root_fd); - close_on_exec_on() took care of this */
733 		}
734 		/* Child expects directory to list on fd #3 */
735 		xmove_fd(cur_fd, 3);
736 		execv(bb_busybox_exec_path, (char**) argv);
737 		_exit(127);
738 #endif
739 	}
740 
741 	/* parent */
742 	close(outfd.wr);
743 	return outfd.rd;
744 }
745 
746 enum {
747 	USE_CTRL_CONN = 1,
748 	LONG_LISTING = 2,
749 };
750 
751 static void
handle_dir_common(int opts)752 handle_dir_common(int opts)
753 {
754 	FILE *ls_fp;
755 	char *line;
756 	int ls_fd;
757 
758 	if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
759 		return; /* port_or_pasv_was_seen emitted error response */
760 
761 	ls_fd = popen_ls((opts & LONG_LISTING) ? "-lA" : "-1A");
762 	ls_fp = xfdopen_for_read(ls_fd);
763 /* FIXME: filenames with embedded newlines are mishandled */
764 
765 	if (opts & USE_CTRL_CONN) {
766 		/* STAT <filename> */
767 		cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
768 		while (1) {
769 			line = xmalloc_fgetline(ls_fp);
770 			if (!line)
771 				break;
772 			/* Hack: 0 results in no status at all */
773 			/* Note: it's ok that we don't prepend space,
774 			 * ftp.kernel.org doesn't do that too */
775 			cmdio_write(0, line);
776 			free(line);
777 		}
778 		WRITE_OK(FTP_STATFILE_OK);
779 	} else {
780 		/* LIST/NLST [<filename>] */
781 		int remote_fd = get_remote_transfer_fd(" Directory listing");
782 		if (remote_fd >= 0) {
783 			while (1) {
784 				unsigned len;
785 
786 				line = xmalloc_fgets(ls_fp);
787 				if (!line)
788 					break;
789 				/* I've seen clients complaining when they
790 				 * are fed with ls output with bare '\n'.
791 				 * Replace trailing "\n\0" with "\r\n".
792 				 */
793 				len = strlen(line);
794 				if (len != 0) /* paranoia check */
795 					line[len - 1] = '\r';
796 				line[len] = '\n';
797 				xwrite(remote_fd, line, len + 1);
798 				free(line);
799 			}
800 		}
801 		close(remote_fd);
802 		WRITE_OK(FTP_TRANSFEROK);
803 	}
804 	fclose(ls_fp); /* closes ls_fd too */
805 }
806 static void
handle_list(void)807 handle_list(void)
808 {
809 	handle_dir_common(LONG_LISTING);
810 }
811 static void
handle_nlst(void)812 handle_nlst(void)
813 {
814 	/* NLST returns list of names, "\r\n" terminated without regard
815 	 * to the current binary flag. Names may start with "/",
816 	 * then they represent full names (we don't produce such names),
817 	 * otherwise names are relative to current directory.
818 	 * Embedded "\n" are replaced by NULs. This is safe since names
819 	 * can never contain NUL.
820 	 */
821 	handle_dir_common(0);
822 }
823 static void
handle_stat_file(void)824 handle_stat_file(void)
825 {
826 	handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
827 }
828 
829 /* This can be extended to handle MLST, as all info is available
830  * in struct stat for that:
831  * MLST file_name
832  * 250-Listing file_name
833  *  type=file;size=4161;modify=19970214165800; /dir/dir/file_name
834  * 250 End
835  * Nano-doc:
836  * MLST [<file or dir name, "." assumed if not given>]
837  * Returned name should be either the same as requested, or fully qualified.
838  * If there was no parameter, return "" or (preferred) fully-qualified name.
839  * Returned "facts" (case is not important):
840  *  size    - size in octets
841  *  modify  - last modification time
842  *  type    - entry type (file,dir,OS.unix=block)
843  *            (+ cdir and pdir types for MLSD)
844  *  unique  - unique id of file/directory (inode#)
845  *  perm    -
846  *      a: can be appended to (APPE)
847  *      d: can be deleted (RMD/DELE)
848  *      f: can be renamed (RNFR)
849  *      r: can be read (RETR)
850  *      w: can be written (STOR)
851  *      e: can CWD into this dir
852  *      l: this dir can be listed (dir only!)
853  *      c: can create files in this dir
854  *      m: can create dirs in this dir (MKD)
855  *      p: can delete files in this dir
856  *  UNIX.mode - unix file mode
857  */
858 static void
handle_size_or_mdtm(int need_size)859 handle_size_or_mdtm(int need_size)
860 {
861 	struct stat statbuf;
862 	struct tm broken_out;
863 	char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
864 		| sizeof("NNN YYYYMMDDhhmmss\r\n")
865 	];
866 
867 	if (!G.ftp_arg
868 	 || stat(G.ftp_arg, &statbuf) != 0
869 	 || !S_ISREG(statbuf.st_mode)
870 	) {
871 		WRITE_ERR(FTP_FILEFAIL);
872 		return;
873 	}
874 	if (need_size) {
875 		sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
876 	} else {
877 		gmtime_r(&statbuf.st_mtime, &broken_out);
878 		sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
879 			broken_out.tm_year + 1900,
880 			broken_out.tm_mon + 1,
881 			broken_out.tm_mday,
882 			broken_out.tm_hour,
883 			broken_out.tm_min,
884 			broken_out.tm_sec);
885 	}
886 	cmdio_write_raw(buf);
887 }
888 
889 /* Upload commands */
890 
891 #if ENABLE_FEATURE_FTPD_WRITE
892 static void
handle_mkd(void)893 handle_mkd(void)
894 {
895 	if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
896 		WRITE_ERR(FTP_FILEFAIL);
897 		return;
898 	}
899 	WRITE_OK(FTP_MKDIROK);
900 }
901 
902 static void
handle_rmd(void)903 handle_rmd(void)
904 {
905 	if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
906 		WRITE_ERR(FTP_FILEFAIL);
907 		return;
908 	}
909 	WRITE_OK(FTP_RMDIROK);
910 }
911 
912 static void
handle_dele(void)913 handle_dele(void)
914 {
915 	if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
916 		WRITE_ERR(FTP_FILEFAIL);
917 		return;
918 	}
919 	WRITE_OK(FTP_DELEOK);
920 }
921 
922 static void
handle_rnfr(void)923 handle_rnfr(void)
924 {
925 	free(G.rnfr_filename);
926 	G.rnfr_filename = xstrdup(G.ftp_arg);
927 	WRITE_OK(FTP_RNFROK);
928 }
929 
930 static void
handle_rnto(void)931 handle_rnto(void)
932 {
933 	int retval;
934 
935 	/* If we didn't get a RNFR, throw a wobbly */
936 	if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
937 		cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
938 		return;
939 	}
940 
941 	retval = rename(G.rnfr_filename, G.ftp_arg);
942 	free(G.rnfr_filename);
943 	G.rnfr_filename = NULL;
944 
945 	if (retval) {
946 		WRITE_ERR(FTP_FILEFAIL);
947 		return;
948 	}
949 	WRITE_OK(FTP_RENAMEOK);
950 }
951 
952 static void
handle_upload_common(int is_append,int is_unique)953 handle_upload_common(int is_append, int is_unique)
954 {
955 	struct stat statbuf;
956 	char *tempname;
957 	off_t bytes_transferred;
958 	off_t offset;
959 	int local_file_fd;
960 	int remote_fd;
961 
962 	offset = G.restart_pos;
963 	G.restart_pos = 0;
964 
965 	if (!port_or_pasv_was_seen())
966 		return; /* port_or_pasv_was_seen emitted error response */
967 
968 	tempname = NULL;
969 	local_file_fd = -1;
970 	if (is_unique) {
971 		tempname = xstrdup(" FILE: uniq.XXXXXX");
972 		local_file_fd = mkstemp(tempname + 7);
973 	} else if (G.ftp_arg) {
974 		int flags = O_WRONLY | O_CREAT | O_TRUNC;
975 		if (is_append)
976 			flags = O_WRONLY | O_CREAT | O_APPEND;
977 		if (offset)
978 			flags = O_WRONLY | O_CREAT;
979 		local_file_fd = open(G.ftp_arg, flags, 0666);
980 	}
981 
982 	if (local_file_fd < 0
983 	 || fstat(local_file_fd, &statbuf) != 0
984 	 || !S_ISREG(statbuf.st_mode)
985 	) {
986 		free(tempname);
987 		WRITE_ERR(FTP_UPLOADFAIL);
988 		if (local_file_fd >= 0)
989 			goto close_local_and_bail;
990 		return;
991 	}
992 	G.local_file_fd = local_file_fd;
993 
994 	if (offset)
995 		xlseek(local_file_fd, offset, SEEK_SET);
996 
997 	remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
998 	free(tempname);
999 
1000 	if (remote_fd < 0)
1001 		goto close_local_and_bail;
1002 
1003 	bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
1004 	close(remote_fd);
1005 	if (bytes_transferred < 0)
1006 		WRITE_ERR(FTP_BADSENDFILE);
1007 	else
1008 		WRITE_OK(FTP_TRANSFEROK);
1009 
1010  close_local_and_bail:
1011 	close(local_file_fd);
1012 	G.local_file_fd = 0;
1013 }
1014 
1015 static void
handle_stor(void)1016 handle_stor(void)
1017 {
1018 	handle_upload_common(0, 0);
1019 }
1020 
1021 static void
handle_appe(void)1022 handle_appe(void)
1023 {
1024 	G.restart_pos = 0;
1025 	handle_upload_common(1, 0);
1026 }
1027 
1028 static void
handle_stou(void)1029 handle_stou(void)
1030 {
1031 	G.restart_pos = 0;
1032 	handle_upload_common(0, 1);
1033 }
1034 #endif /* ENABLE_FEATURE_FTPD_WRITE */
1035 
1036 static uint32_t
cmdio_get_cmd_and_arg(void)1037 cmdio_get_cmd_and_arg(void)
1038 {
1039 	int len;
1040 	uint32_t cmdval;
1041 	char *cmd;
1042 
1043 	alarm(G.timeout);
1044 
1045 	free(G.ftp_cmd);
1046 	{
1047 		/* Paranoia. Peer may send 1 gigabyte long cmd... */
1048 		/* Using separate len_on_stk instead of len optimizes
1049 		 * code size (allows len to be in CPU register) */
1050 		size_t len_on_stk = 8 * 1024;
1051 		G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
1052 		if (!cmd)
1053 			exit(0);
1054 		len = len_on_stk;
1055 	}
1056 
1057 	/* De-escape telnet: 0xff,0xff => 0xff */
1058 	/* RFC959 says that ABOR, STAT, QUIT may be sent even during
1059 	 * data transfer, and may be preceded by telnet's "Interrupt Process"
1060 	 * code (two-byte sequence 255,244) and then by telnet "Synch" code
1061 	 * 255,242 (byte 242 is sent with TCP URG bit using send(MSG_OOB)
1062 	 * and may generate SIGURG on our side. See RFC854).
1063 	 * So far we don't support that (may install SIGURG handler if we'd want to),
1064 	 * but we need to at least remove 255,xxx pairs. lftp sends those. */
1065 	/* Then de-escape FTP: NUL => '\n' */
1066 	/* Testing for \xff:
1067 	 * Create file named '\xff': echo Hello >`echo -ne "\xff"`
1068 	 * Try to get it:            ftpget -v 127.0.0.1 Eff `echo -ne "\xff\xff"`
1069 	 * (need "\xff\xff" until ftpget applet is fixed to do escaping :)
1070 	 * Testing for embedded LF:
1071 	 * LF_HERE=`echo -ne "LF\nHERE"`
1072 	 * echo Hello >"$LF_HERE"
1073 	 * ftpget -v 127.0.0.1 LF_HERE "$LF_HERE"
1074 	 */
1075 	{
1076 		int dst, src;
1077 
1078 		/* Strip "\r\n" if it is there */
1079 		if (len != 0 && cmd[len - 1] == '\n') {
1080 			len--;
1081 			if (len != 0 && cmd[len - 1] == '\r')
1082 				len--;
1083 			cmd[len] = '\0';
1084 		}
1085 		src = strchrnul(cmd, 0xff) - cmd;
1086 		/* 99,99% there are neither NULs nor 255s and src == len */
1087 		if (src < len) {
1088 			dst = src;
1089 			do {
1090 				if ((unsigned char)(cmd[src]) == 255) {
1091 					src++;
1092 					/* 255,xxx - skip 255 */
1093 					if ((unsigned char)(cmd[src]) != 255) {
1094 						/* 255,!255 - skip both */
1095 						src++;
1096 						continue;
1097 					}
1098 					/* 255,255 - retain one 255 */
1099 				}
1100 				/* NUL => '\n' */
1101 				cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1102 				src++;
1103 			} while (src < len);
1104 			cmd[dst] = '\0';
1105 		}
1106 	}
1107 
1108 	if (G.verbose > 1)
1109 		verbose_log(cmd);
1110 
1111 	G.ftp_arg = strchr(cmd, ' ');
1112 	if (G.ftp_arg != NULL)
1113 		*G.ftp_arg++ = '\0';
1114 
1115 	/* Uppercase and pack into uint32_t first word of the command */
1116 	cmdval = 0;
1117 	while (*cmd)
1118 		cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1119 
1120 	return cmdval;
1121 }
1122 
1123 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1124 #define mk_const3(a,b,c)    ((a * 0x100 + b) * 0x100 + c)
1125 enum {
1126 	const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1127 	const_APPE = mk_const4('A', 'P', 'P', 'E'),
1128 	const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1129 	const_CWD  = mk_const3('C', 'W', 'D'),
1130 	const_DELE = mk_const4('D', 'E', 'L', 'E'),
1131 	const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1132 	const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1133 	const_HELP = mk_const4('H', 'E', 'L', 'P'),
1134 	const_LIST = mk_const4('L', 'I', 'S', 'T'),
1135 	const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1136 	const_MKD  = mk_const3('M', 'K', 'D'),
1137 	const_MODE = mk_const4('M', 'O', 'D', 'E'),
1138 	const_NLST = mk_const4('N', 'L', 'S', 'T'),
1139 	const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1140 	const_PASS = mk_const4('P', 'A', 'S', 'S'),
1141 	const_PASV = mk_const4('P', 'A', 'S', 'V'),
1142 	const_PORT = mk_const4('P', 'O', 'R', 'T'),
1143 	const_PWD  = mk_const3('P', 'W', 'D'),
1144 	/* Same as PWD. Reportedly used by windows ftp client */
1145 	const_XPWD = mk_const4('X', 'P', 'W', 'D'),
1146 	const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1147 	const_REST = mk_const4('R', 'E', 'S', 'T'),
1148 	const_RETR = mk_const4('R', 'E', 'T', 'R'),
1149 	const_RMD  = mk_const3('R', 'M', 'D'),
1150 	const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1151 	const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1152 	const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1153 	const_STAT = mk_const4('S', 'T', 'A', 'T'),
1154 	const_STOR = mk_const4('S', 'T', 'O', 'R'),
1155 	const_STOU = mk_const4('S', 'T', 'O', 'U'),
1156 	const_STRU = mk_const4('S', 'T', 'R', 'U'),
1157 	const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1158 	const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1159 	const_USER = mk_const4('U', 'S', 'E', 'R'),
1160 
1161 #if !BB_MMU
1162 	OPT_l = (1 << 0),
1163 	OPT_1 = (1 << 1),
1164 #endif
1165 	BIT_A =        (!BB_MMU) * 2,
1166 	OPT_A = (1 << (BIT_A + 0)),
1167 	OPT_v = (1 << (BIT_A + 1)),
1168 	OPT_S = (1 << (BIT_A + 2)),
1169 	OPT_w = (1 << (BIT_A + 3)) * ENABLE_FEATURE_FTPD_WRITE,
1170 };
1171 
1172 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
ftpd_main(int argc UNUSED_PARAM,char ** argv)1173 int ftpd_main(int argc UNUSED_PARAM, char **argv)
1174 {
1175 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1176 	struct passwd *pw = NULL;
1177 	char *anon_opt = NULL;
1178 #endif
1179 	unsigned abs_timeout;
1180 	unsigned verbose_S;
1181 	smallint opts;
1182 
1183 	INIT_G();
1184 
1185 	abs_timeout = 1 * 60 * 60;
1186 	verbose_S = 0;
1187 	G.timeout = 2 * 60;
1188 #if BB_MMU
1189 	opts = getopt32(argv, "^"   "AvS" IF_FEATURE_FTPD_WRITE("w")
1190 		"t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1191 		"\0" "vv:SS",
1192 		&G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1193 		&G.verbose, &verbose_S
1194 	);
1195 #else
1196 	opts = getopt32(argv, "^" "l1AvS" IF_FEATURE_FTPD_WRITE("w")
1197 		"t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1198 		"\0" "vv:SS",
1199 		&G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1200 		&G.verbose, &verbose_S
1201 	);
1202 	if (opts & (OPT_l|OPT_1)) {
1203 		/* Our secret backdoor to ls: see popen_ls() */
1204 		if (fchdir(3) != 0)
1205 			_exit(127);
1206 		/* memset(&G, 0, sizeof(G)); - ls_main does it */
1207 		/* NB: in this case -A has a different meaning: like "ls -A" */
1208 		return ls_main(/*argc_unused*/ 0, argv);
1209 	}
1210 #endif
1211 	if (G.verbose < verbose_S)
1212 		G.verbose = verbose_S;
1213 	if (abs_timeout | G.timeout) {
1214 		if (abs_timeout == 0)
1215 			abs_timeout = INT_MAX;
1216 		G.end_time = monotonic_sec() + abs_timeout;
1217 		if (G.timeout > abs_timeout)
1218 			G.timeout = abs_timeout;
1219 	}
1220 	strcpy(G.msg_ok  + 4, MSG_OK );
1221 	strcpy(G.msg_err + 4, MSG_ERR);
1222 
1223 	G.local_addr = get_sock_lsa(STDIN_FILENO);
1224 	if (!G.local_addr) {
1225 		/* This is confusing:
1226 		 * bb_error_msg_and_die("stdin is not a socket");
1227 		 * Better: */
1228 		bb_show_usage();
1229 		/* Help text says that ftpd must be used as inetd service,
1230 		 * which is by far the most usual cause of get_sock_lsa
1231 		 * failure */
1232 	}
1233 
1234 	if (!(opts & OPT_v))
1235 		logmode = LOGMODE_NONE;
1236 	if (opts & OPT_S) {
1237 		/* LOG_NDELAY is needed since we may chroot later */
1238 		openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1239 		logmode |= LOGMODE_SYSLOG;
1240 	}
1241 	if (logmode)
1242 		applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1243 
1244 	//umask(077); - admin can set umask before starting us
1245 
1246 	/* Signals */
1247 	bb_signals(0
1248 		/* We'll always take EPIPE rather than a rude signal, thanks */
1249 		+ (1 << SIGPIPE)
1250 		/* LIST command spawns chilren. Prevent zombies */
1251 		+ (1 << SIGCHLD)
1252 		, SIG_IGN);
1253 
1254 	/* Set up options on the command socket (do we need these all? why?) */
1255 	setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY);
1256 	setsockopt_keepalive(STDIN_FILENO);
1257 	/* Telnet protocol over command link may send "urgent" data,
1258 	 * we prefer it to be received in the "normal" data stream: */
1259 	setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE);
1260 
1261 	WRITE_OK(FTP_GREET);
1262 	signal(SIGALRM, timeout_handler);
1263 
1264 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1265 	if (!(opts & OPT_A)) {
1266 		while (1) {
1267 			uint32_t cmdval = cmdio_get_cmd_and_arg();
1268 			if (cmdval == const_USER) {
1269 				if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) {
1270 					pw = getpwnam(anon_opt);
1271 					if (pw)
1272 						break; /* does not even ask for password */
1273 				}
1274 				pw = getpwnam(G.ftp_arg);
1275 				cmdio_write_raw(STR(FTP_GIVEPWORD)" Specify password\r\n");
1276 			} else if (cmdval == const_PASS) {
1277 				if (check_password(pw, G.ftp_arg) > 0) {
1278 					break;	/* login success */
1279 				}
1280 				cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1281 				pw = NULL;
1282 			} else if (cmdval == const_QUIT) {
1283 				WRITE_OK(FTP_GOODBYE);
1284 				return 0;
1285 			} else {
1286 				cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER+PASS\r\n");
1287 			}
1288 		}
1289 		WRITE_OK(FTP_LOGINOK);
1290 	}
1291 #endif
1292 
1293 	/* Do this after auth, else /etc/passwd is not accessible */
1294 #if !BB_MMU
1295 	G.root_fd = -1;
1296 #endif
1297 	argv += optind;
1298 	if (argv[0]) {
1299 		const char *basedir = argv[0];
1300 #if !BB_MMU
1301 		G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1302 		close_on_exec_on(G.root_fd);
1303 #endif
1304 		if (chroot(basedir) == 0)
1305 			basedir = "/";
1306 #if !BB_MMU
1307 		else {
1308 			close(G.root_fd);
1309 			G.root_fd = -1;
1310 		}
1311 #endif
1312 		/*
1313 		 * If chroot failed, assume that we aren't root,
1314 		 * and at least chdir to the specified DIR
1315 		 * (older versions were dying with error message).
1316 		 * If chroot worked, move current dir to new "/":
1317 		 */
1318 		xchdir(basedir);
1319 	}
1320 
1321 #if ENABLE_FEATURE_FTPD_AUTHENTICATION
1322 	if (pw)
1323 		change_identity(pw);
1324 	/* else: -A is in effect */
1325 #endif
1326 
1327 	/* RFC-959 Section 5.1
1328 	 * The following commands and options MUST be supported by every
1329 	 * server-FTP and user-FTP, except in cases where the underlying
1330 	 * file system or operating system does not allow or support
1331 	 * a particular command.
1332 	 * Type: ASCII Non-print, IMAGE, LOCAL 8
1333 	 * Mode: Stream
1334 	 * Structure: File, Record*
1335 	 * (Record structure is REQUIRED only for hosts whose file
1336 	 *  systems support record structure).
1337 	 * Commands:
1338 	 * USER, PASS, ACCT, [bbox: ACCT not supported]
1339 	 * PORT, PASV,
1340 	 * TYPE, MODE, STRU,
1341 	 * RETR, STOR, APPE,
1342 	 * RNFR, RNTO, DELE,
1343 	 * CWD,  CDUP, RMD,  MKD,  PWD,
1344 	 * LIST, NLST,
1345 	 * SYST, STAT,
1346 	 * HELP, NOOP, QUIT.
1347 	 */
1348 	/* ACCOUNT (ACCT)
1349 	 * "The argument field is a Telnet string identifying the user's account.
1350 	 * The command is not necessarily related to the USER command, as some
1351 	 * sites may require an account for login and others only for specific
1352 	 * access, such as storing files. In the latter case the command may
1353 	 * arrive at any time.
1354 	 * There are reply codes to differentiate these cases for the automation:
1355 	 * when account information is required for login, the response to
1356 	 * a successful PASSword command is reply code 332. On the other hand,
1357 	 * if account information is NOT required for login, the reply to
1358 	 * a successful PASSword command is 230; and if the account information
1359 	 * is needed for a command issued later in the dialogue, the server
1360 	 * should return a 332 or 532 reply depending on whether it stores
1361 	 * (pending receipt of the ACCounT command) or discards the command,
1362 	 * respectively."
1363 	 */
1364 
1365 	while (1) {
1366 		uint32_t cmdval = cmdio_get_cmd_and_arg();
1367 
1368 		if (cmdval == const_QUIT) {
1369 			WRITE_OK(FTP_GOODBYE);
1370 			return 0;
1371 		}
1372 		else if (cmdval == const_USER)
1373 			/* This would mean "ok, now give me PASS". */
1374 			/*WRITE_OK(FTP_GIVEPWORD);*/
1375 			/* vsftpd can be configured to not require that,
1376 			 * and this also saves one roundtrip:
1377 			 */
1378 			WRITE_OK(FTP_LOGINOK);
1379 		else if (cmdval == const_PASS)
1380 			WRITE_OK(FTP_LOGINOK);
1381 		else if (cmdval == const_NOOP)
1382 			WRITE_OK(FTP_NOOPOK);
1383 		else if (cmdval == const_TYPE)
1384 			WRITE_OK(FTP_TYPEOK);
1385 		else if (cmdval == const_STRU)
1386 			WRITE_OK(FTP_STRUOK);
1387 		else if (cmdval == const_MODE)
1388 			WRITE_OK(FTP_MODEOK);
1389 		else if (cmdval == const_ALLO)
1390 			WRITE_OK(FTP_ALLOOK);
1391 		else if (cmdval == const_SYST)
1392 			cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1393 		else if (cmdval == const_PWD || cmdval == const_XPWD)
1394 			handle_pwd();
1395 		else if (cmdval == const_CWD)
1396 			handle_cwd();
1397 		else if (cmdval == const_CDUP) /* cd .. */
1398 			handle_cdup();
1399 		/* HELP is nearly useless, but we can reuse FEAT for it */
1400 		/* lftp uses FEAT */
1401 		else if (cmdval == const_HELP || cmdval == const_FEAT)
1402 			handle_feat(cmdval == const_HELP
1403 					? STRNUM32(FTP_HELP)
1404 					: STRNUM32(FTP_STATOK)
1405 			);
1406 		else if (cmdval == const_LIST) /* ls -l */
1407 			handle_list();
1408 		else if (cmdval == const_NLST) /* "name list", bare ls */
1409 			handle_nlst();
1410 		/* SIZE is crucial for wget's download indicator etc */
1411 		/* Mozilla, lftp use MDTM (presumably for caching) */
1412 		else if (cmdval == const_SIZE || cmdval == const_MDTM)
1413 			handle_size_or_mdtm(cmdval == const_SIZE);
1414 		else if (cmdval == const_STAT) {
1415 			if (G.ftp_arg == NULL)
1416 				handle_stat();
1417 			else
1418 				handle_stat_file();
1419 		}
1420 		else if (cmdval == const_PASV)
1421 			handle_pasv();
1422 		else if (cmdval == const_EPSV)
1423 			handle_epsv();
1424 		else if (cmdval == const_RETR)
1425 			handle_retr();
1426 		else if (cmdval == const_PORT)
1427 			handle_port();
1428 		else if (cmdval == const_REST)
1429 			handle_rest();
1430 #if ENABLE_FEATURE_FTPD_WRITE
1431 		else if (opts & OPT_w) {
1432 			if (cmdval == const_STOR)
1433 				handle_stor();
1434 			else if (cmdval == const_MKD)
1435 				handle_mkd();
1436 			else if (cmdval == const_RMD)
1437 				handle_rmd();
1438 			else if (cmdval == const_DELE)
1439 				handle_dele();
1440 			else if (cmdval == const_RNFR) /* "rename from" */
1441 				handle_rnfr();
1442 			else if (cmdval == const_RNTO) /* "rename to" */
1443 				handle_rnto();
1444 			else if (cmdval == const_APPE)
1445 				handle_appe();
1446 			else if (cmdval == const_STOU) /* "store unique" */
1447 				handle_stou();
1448 			else
1449 				goto bad_cmd;
1450 		}
1451 #endif
1452 #if 0
1453 		else if (cmdval == const_STOR
1454 		 || cmdval == const_MKD
1455 		 || cmdval == const_RMD
1456 		 || cmdval == const_DELE
1457 		 || cmdval == const_RNFR
1458 		 || cmdval == const_RNTO
1459 		 || cmdval == const_APPE
1460 		 || cmdval == const_STOU
1461 		) {
1462 			cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1463 		}
1464 #endif
1465 		else {
1466 			/* Which unsupported commands were seen in the wild?
1467 			 * (doesn't necessarily mean "we must support them")
1468 			 * foo 1.2.3: XXXX - comment
1469 			 */
1470 #if ENABLE_FEATURE_FTPD_WRITE
1471  bad_cmd:
1472 #endif
1473 			cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
1474 		}
1475 	}
1476 }
1477