ftpd: add idle and absolute timeouts. This is a security issue,
[oweals/busybox.git] / networking / ftpd.c
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 tarball for details.
8  *
9  * Only subset of FTP protocol is implemented but vast majority of clients
10  * should not have any problem. You have to run this daemon via inetd.
11  */
12
13 #include "libbb.h"
14 #include <syslog.h>
15 #include <netinet/tcp.h>
16
17 #define FTP_DATACONN            150
18 #define FTP_NOOPOK              200
19 #define FTP_TYPEOK              200
20 #define FTP_PORTOK              200
21 #define FTP_STRUOK              200
22 #define FTP_MODEOK              200
23 #define FTP_ALLOOK              202
24 #define FTP_STATOK              211
25 #define FTP_STATFILE_OK         213
26 #define FTP_HELP                214
27 #define FTP_SYSTOK              215
28 #define FTP_GREET               220
29 #define FTP_GOODBYE             221
30 #define FTP_TRANSFEROK          226
31 #define FTP_PASVOK              227
32 /*#define FTP_EPRTOK              228*/
33 #define FTP_EPSVOK              229
34 #define FTP_LOGINOK             230
35 #define FTP_CWDOK               250
36 #define FTP_RMDIROK             250
37 #define FTP_DELEOK              250
38 #define FTP_RENAMEOK            250
39 #define FTP_PWDOK               257
40 #define FTP_MKDIROK             257
41 #define FTP_GIVEPWORD           331
42 #define FTP_RESTOK              350
43 #define FTP_RNFROK              350
44 #define FTP_TIMEOUT             421
45 #define FTP_BADSENDCONN         425
46 #define FTP_BADSENDNET          426
47 #define FTP_BADSENDFILE         451
48 #define FTP_BADCMD              500
49 #define FTP_COMMANDNOTIMPL      502
50 #define FTP_NEEDUSER            503
51 #define FTP_NEEDRNFR            503
52 #define FTP_BADSTRU             504
53 #define FTP_BADMODE             504
54 #define FTP_LOGINERR            530
55 #define FTP_FILEFAIL            550
56 #define FTP_NOPERM              550
57 #define FTP_UPLOADFAIL          553
58
59 #define STR1(s) #s
60 #define STR(s) STR1(s)
61
62 /* Convert a constant to 3-digit string, packed into uint32_t */
63 enum {
64         /* Shift for Nth decimal digit */
65         SHIFT2 =  0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
66         SHIFT1 =  8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
67         SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
68 };
69 #define STRNUM32(s) (uint32_t)(0 \
70         | (('0' + ((s) / 1 % 10)) << SHIFT0) \
71         | (('0' + ((s) / 10 % 10)) << SHIFT1) \
72         | (('0' + ((s) / 100 % 10)) << SHIFT2) \
73 )
74
75 struct globals {
76         int pasv_listen_fd;
77         int proc_self_fd;
78         int local_file_fd;
79         int start_time;
80         int abs_timeout;
81         int timeout;
82         off_t local_file_pos;
83         off_t restart_pos;
84         len_and_sockaddr *local_addr;
85         len_and_sockaddr *port_addr;
86         char *ftp_cmd;
87         char *ftp_arg;
88 #if ENABLE_FEATURE_FTP_WRITE
89         char *rnfr_filename;
90 #endif
91 };
92 #define G (*(struct globals*)&bb_common_bufsiz1)
93 #define INIT_G() do { } while (0)
94
95
96 static char *
97 escape_text(const char *prepend, const char *str, unsigned escapee)
98 {
99         unsigned retlen, remainlen, chunklen;
100         char *ret, *found;
101         char append;
102
103         append = (char)escapee;
104         escapee >>= 8;
105
106         remainlen = strlen(str);
107         retlen = strlen(prepend);
108         ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
109         strcpy(ret, prepend);
110
111         for (;;) {
112                 found = strchrnul(str, escapee);
113                 chunklen = found - str + 1;
114
115                 /* Copy chunk up to and including escapee (or NUL) to ret */
116                 memcpy(ret + retlen, str, chunklen);
117                 retlen += chunklen;
118
119                 if (*found == '\0') {
120                         /* It wasn't escapee, it was NUL! */
121                         ret[retlen - 1] = append; /* replace NUL */
122                         ret[retlen] = '\0'; /* add NUL */
123                         break;
124                 }
125                 ret[retlen++] = escapee; /* duplicate escapee */
126                 str = found + 1;
127         }
128         return ret;
129 }
130
131 /* Returns strlen as a bonus */
132 static unsigned
133 replace_char(char *str, char from, char to)
134 {
135         char *p = str;
136         while (*p) {
137                 if (*p == from)
138                         *p = to;
139                 p++;
140         }
141         return p - str;
142 }
143
144 /* NB: status_str is char[4] packed into uint32_t */
145 static void
146 cmdio_write(uint32_t status_str, const char *str)
147 {
148         char *response;
149         int len;
150
151         /* FTP allegedly uses telnet protocol for command link.
152          * In telnet, 0xff is an escape char, and needs to be escaped: */
153         response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
154
155         /* ?! does FTP send embedded LFs as NULs? wow */
156         len = replace_char(response, '\n', '\0');
157
158         response[len++] = '\n'; /* tack on trailing '\n' */
159         xwrite(STDOUT_FILENO, response, len);
160         free(response);
161 }
162
163 static void
164 cmdio_write_ok(int status)
165 {
166         fdprintf(STDOUT_FILENO, "%u Operation successful\r\n", status);
167 }
168
169 /* TODO: output strerr(errno) if errno != 0? */
170 static void
171 cmdio_write_error(int status)
172 {
173         fdprintf(STDOUT_FILENO, "%u Error\r\n", status);
174 }
175
176 static void
177 cmdio_write_raw(const char *p_text)
178 {
179         xwrite_str(STDOUT_FILENO, p_text);
180 }
181
182 static void
183 timeout_handler(int sig UNUSED_PARAM)
184 {
185         off_t pos;
186         int sv_errno = errno;
187
188         if (monotonic_sec() - G.start_time > G.abs_timeout)
189                 goto timed_out;
190
191         if (!G.local_file_fd)
192                 goto timed_out;
193
194         pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
195         if (pos == G.local_file_pos)
196                 goto timed_out;
197         G.local_file_pos = pos;
198
199         alarm(G.timeout);
200         errno = sv_errno;
201         return;
202
203  timed_out:
204         cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
205 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
206         exit(1);
207 }
208
209 /* Simple commands */
210
211 static void
212 handle_pwd(void)
213 {
214         char *cwd, *response;
215
216         cwd = xrealloc_getcwd_or_warn(NULL);
217         if (cwd == NULL)
218                 cwd = xstrdup("");
219
220         /* We have to promote each " to "" */
221         response = escape_text(" \"", cwd, ('"' << 8) + '"');
222         free(cwd);
223         cmdio_write(STRNUM32(FTP_PWDOK), response);
224         free(response);
225 }
226
227 static void
228 handle_cwd(void)
229 {
230         if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
231                 cmdio_write_error(FTP_FILEFAIL);
232                 return;
233         }
234         cmdio_write_ok(FTP_CWDOK);
235 }
236
237 static void
238 handle_cdup(void)
239 {
240         G.ftp_arg = (char*)"..";
241         handle_cwd();
242 }
243
244 static void
245 handle_stat(void)
246 {
247         cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n"
248                         " TYPE: BINARY\r\n"
249                         STR(FTP_STATOK)" Ok\r\n");
250 }
251
252 /* TODO: implement FEAT. Example:
253 # nc -vvv ftp.kernel.org 21
254 ftp.kernel.org (130.239.17.4:21) open
255 220 Welcome to ftp.kernel.org.
256 FEAT
257 211-Features:
258  EPRT
259  EPSV
260  MDTM
261  PASV
262  REST STREAM
263  SIZE
264  TVFS
265  UTF8
266 211 End
267 HELP
268 214-The following commands are recognized.
269  ABOR ACCT ALLO APPE CDUP CWD  DELE EPRT EPSV FEAT HELP LIST MDTM MKD
270  MODE NLST NOOP OPTS PASS PASV PORT PWD  QUIT REIN REST RETR RMD  RNFR
271  RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
272  XPWD XRMD
273 214 Help OK.
274 */
275 static void
276 handle_help(void)
277 {
278         cmdio_write_raw(STR(FTP_HELP)"-Commands:\r\n"
279                         " ALLO CDUP CWD EPSV HELP LIST\r\n"
280                         " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n"
281                         " REST RETR SIZE STAT STRU SYST TYPE USER\r\n"
282 #if ENABLE_FEATURE_FTP_WRITE
283                         " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n"
284 #endif
285                         STR(FTP_HELP)" Ok\r\n");
286 }
287
288 /* Download commands */
289
290 static inline int
291 port_active(void)
292 {
293         return (G.port_addr != NULL);
294 }
295
296 static inline int
297 pasv_active(void)
298 {
299         return (G.pasv_listen_fd > STDOUT_FILENO);
300 }
301
302 static void
303 port_pasv_cleanup(void)
304 {
305         free(G.port_addr);
306         G.port_addr = NULL;
307         if (G.pasv_listen_fd > STDOUT_FILENO)
308                 close(G.pasv_listen_fd);
309         G.pasv_listen_fd = -1;
310 }
311
312 /* On error, emits error code to the peer */
313 static int
314 ftpdataio_get_pasv_fd(void)
315 {
316         int remote_fd;
317
318         remote_fd = accept(G.pasv_listen_fd, NULL, 0);
319
320         if (remote_fd < 0) {
321                 cmdio_write_error(FTP_BADSENDCONN);
322                 return remote_fd;
323         }
324
325         setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
326         return remote_fd;
327 }
328
329 /* Clears port/pasv data.
330  * This means we dont waste resources, for example, keeping
331  * PASV listening socket open when it is no longer needed.
332  * On error, emits error code to the peer (or exits).
333  * On success, emits p_status_msg to the peer.
334  */
335 static int
336 get_remote_transfer_fd(const char *p_status_msg)
337 {
338         int remote_fd;
339
340         if (pasv_active())
341                 /* On error, emits error code to the peer */
342                 remote_fd = ftpdataio_get_pasv_fd();
343         else
344                 /* Exits on error */
345                 remote_fd = xconnect_stream(G.port_addr);
346
347         port_pasv_cleanup();
348
349         if (remote_fd < 0)
350                 return remote_fd;
351
352         cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
353         return remote_fd;
354 }
355
356 /* If there were neither PASV nor PORT, emits error code to the peer */
357 static int
358 port_or_pasv_was_seen(void)
359 {
360         if (!pasv_active() && !port_active()) {
361                 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n");
362                 return 0;
363         }
364
365         return 1;
366 }
367
368 /* Exits on error */
369 static unsigned
370 bind_for_passive_mode(void)
371 {
372         int fd;
373         unsigned port;
374
375         port_pasv_cleanup();
376
377         G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
378         setsockopt_reuseaddr(fd);
379
380         set_nport(G.local_addr, 0);
381         xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
382         xlisten(fd, 1);
383         getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
384
385         port = get_nport(&G.local_addr->u.sa);
386         port = ntohs(port);
387         return port;
388 }
389
390 /* Exits on error */
391 static void
392 handle_pasv(void)
393 {
394         unsigned port;
395         char *addr, *response;
396
397         port = bind_for_passive_mode();
398
399         if (G.local_addr->u.sa.sa_family == AF_INET)
400                 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
401         else /* seen this in the wild done by other ftp servers: */
402                 addr = xstrdup("0.0.0.0");
403         replace_char(addr, '.', ',');
404
405         response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n",
406                         addr, (int)(port >> 8), (int)(port & 255));
407         free(addr);
408         cmdio_write_raw(response);
409         free(response);
410 }
411
412 /* Exits on error */
413 static void
414 handle_epsv(void)
415 {
416         unsigned port;
417         char *response;
418
419         port = bind_for_passive_mode();
420         response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port);
421         cmdio_write_raw(response);
422         free(response);
423 }
424
425 static void
426 handle_port(void)
427 {
428         unsigned port, port_hi;
429         char *raw, *comma;
430         socklen_t peer_ipv4_len;
431         struct sockaddr_in peer_ipv4;
432         struct in_addr port_ipv4_sin_addr;
433
434         port_pasv_cleanup();
435
436         raw = G.ftp_arg;
437
438         /* PORT command format makes sense only over IPv4 */
439         if (!raw || G.local_addr->u.sa.sa_family != AF_INET) {
440  bail:
441                 cmdio_write_error(FTP_BADCMD);
442                 return;
443         }
444
445         comma = strrchr(raw, ',');
446         if (comma == NULL)
447                 goto bail;
448         *comma = '\0';
449         port = bb_strtou(&comma[1], NULL, 10);
450         if (errno || port > 0xff)
451                 goto bail;
452
453         comma = strrchr(raw, ',');
454         if (comma == NULL)
455                 goto bail;
456         *comma = '\0';
457         port_hi = bb_strtou(&comma[1], NULL, 10);
458         if (errno || port_hi > 0xff)
459                 goto bail;
460         port |= port_hi << 8;
461
462         replace_char(raw, ',', '.');
463
464         /* We are verifying that PORT's IP matches getpeername().
465          * Otherwise peer can make us open data connections
466          * to other hosts (security problem!)
467          * This code would be too simplistic:
468          * lsa = xdotted2sockaddr(raw, port);
469          * if (lsa == NULL) goto bail;
470          */
471         if (!inet_aton(raw, &port_ipv4_sin_addr))
472                 goto bail;
473         peer_ipv4_len = sizeof(peer_ipv4);
474         if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
475                 goto bail;
476         if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
477                 goto bail;
478
479         G.port_addr = xdotted2sockaddr(raw, port);
480         cmdio_write_ok(FTP_PORTOK);
481 }
482
483 static void
484 handle_rest(void)
485 {
486         /* When ftp_arg == NULL simply restart from beginning */
487         G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
488         cmdio_write_ok(FTP_RESTOK);
489 }
490
491 static void
492 handle_retr(void)
493 {
494         struct stat statbuf;
495         off_t bytes_transferred;
496         int remote_fd;
497         int local_file_fd;
498         off_t offset = G.restart_pos;
499         char *response;
500
501         G.restart_pos = 0;
502
503         if (!port_or_pasv_was_seen())
504                 return; /* port_or_pasv_was_seen emitted error response */
505
506         /* O_NONBLOCK is useful if file happens to be a device node */
507         local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
508         if (local_file_fd < 0) {
509                 cmdio_write_error(FTP_FILEFAIL);
510                 return;
511         }
512
513         if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
514                 /* Note - pretend open failed */
515                 cmdio_write_error(FTP_FILEFAIL);
516                 goto file_close_out;
517         }
518         G.local_file_fd = local_file_fd;
519
520         /* Now deactive O_NONBLOCK, otherwise we have a problem
521          * on DMAPI filesystems such as XFS DMAPI.
522          */
523         ndelay_off(local_file_fd);
524
525         /* Set the download offset (from REST) if any */
526         if (offset != 0)
527                 xlseek(local_file_fd, offset, SEEK_SET);
528
529         response = xasprintf(
530                 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)",
531                 G.ftp_arg, statbuf.st_size);
532         remote_fd = get_remote_transfer_fd(response);
533         free(response);
534         if (remote_fd < 0)
535                 goto file_close_out;
536
537         bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
538         close(remote_fd);
539         if (bytes_transferred < 0)
540                 cmdio_write_error(FTP_BADSENDFILE);
541         else
542                 cmdio_write_ok(FTP_TRANSFEROK);
543
544  file_close_out:
545         close(local_file_fd);
546         G.local_file_fd = 0;
547 }
548
549 /* List commands */
550
551 static int
552 popen_ls(const char *opt)
553 {
554         char *cwd;
555         const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
556         struct fd_pair outfd;
557         pid_t pid;
558
559         cwd = xrealloc_getcwd_or_warn(NULL);
560         xpiped_pair(outfd);
561
562         /*fflush(NULL); - so far we dont use stdio on output */
563         pid = vfork();
564         switch (pid) {
565         case -1:  /* failure */
566                 bb_perror_msg_and_die("vfork");
567         case 0:  /* child */
568                 /* NB: close _first_, then move fds! */
569                 close(outfd.rd);
570                 xmove_fd(outfd.wr, STDOUT_FILENO);
571                 close(STDIN_FILENO);
572                 /* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
573                 if (fchdir(G.proc_self_fd) == 0) {
574                         close(G.proc_self_fd);
575                         argv[2] = cwd;
576                         /* ftpd ls helper chdirs to argv[2],
577                          * preventing peer from seeing /proc/self
578                          */
579                         execv("exe", (char**) argv);
580                 }
581                 _exit(127);
582         }
583
584         /* parent */
585         close(outfd.wr);
586         free(cwd);
587         return outfd.rd;
588 }
589
590 enum {
591         USE_CTRL_CONN = 1,
592         LONG_LISTING = 2,
593 };
594
595 static void
596 handle_dir_common(int opts)
597 {
598         FILE *ls_fp;
599         char *line;
600         int ls_fd;
601
602         if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
603                 return; /* port_or_pasv_was_seen emitted error response */
604
605         /* -n prevents user/groupname display,
606          * which can be problematic in chroot */
607         ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
608         ls_fp = fdopen(ls_fd, "r");
609         if (!ls_fp) /* never happens. paranoia */
610                 bb_perror_msg_and_die("fdopen");
611
612         if (opts & USE_CTRL_CONN) {
613                 /* STAT <filename> */
614                 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
615                 while (1) {
616                         line = xmalloc_fgetline(ls_fp);
617                         if (!line)
618                                 break;
619                         cmdio_write(0, line); /* hack: 0 results in no status at all */
620                         free(line);
621                 }
622                 cmdio_write_ok(FTP_STATFILE_OK);
623         } else {
624                 /* LIST/NLST [<filename>] */
625                 int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
626                 if (remote_fd >= 0) {
627                         while (1) {
628                                 line = xmalloc_fgetline(ls_fp);
629                                 if (!line)
630                                         break;
631                                 /* I've seen clients complaining when they
632                                  * are fed with ls output with bare '\n'.
633                                  * Pity... that would be much simpler.
634                                  */
635                                 xwrite_str(remote_fd, line);
636                                 xwrite(remote_fd, "\r\n", 2);
637                                 free(line);
638                         }
639                 }
640                 close(remote_fd);
641                 cmdio_write_ok(FTP_TRANSFEROK);
642         }
643         fclose(ls_fp); /* closes ls_fd too */
644 }
645 static void
646 handle_list(void)
647 {
648         handle_dir_common(LONG_LISTING);
649 }
650 static void
651 handle_nlst(void)
652 {
653         handle_dir_common(0);
654 }
655 static void
656 handle_stat_file(void)
657 {
658         handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
659 }
660
661 static void
662 handle_size(void)
663 {
664         struct stat statbuf;
665         char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3];
666
667         if (!G.ftp_arg
668          || stat(G.ftp_arg, &statbuf) != 0
669          || !S_ISREG(statbuf.st_mode)
670         ) {
671                 cmdio_write_error(FTP_FILEFAIL);
672                 return;
673         }
674         sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
675         cmdio_write_raw(buf);
676 }
677
678 /* Upload commands */
679
680 #if ENABLE_FEATURE_FTP_WRITE
681 static void
682 handle_mkd(void)
683 {
684         if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
685                 cmdio_write_error(FTP_FILEFAIL);
686                 return;
687         }
688         cmdio_write_ok(FTP_MKDIROK);
689 }
690
691 static void
692 handle_rmd(void)
693 {
694         if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
695                 cmdio_write_error(FTP_FILEFAIL);
696                 return;
697         }
698         cmdio_write_ok(FTP_RMDIROK);
699 }
700
701 static void
702 handle_dele(void)
703 {
704         if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
705                 cmdio_write_error(FTP_FILEFAIL);
706                 return;
707         }
708         cmdio_write_ok(FTP_DELEOK);
709 }
710
711 static void
712 handle_rnfr(void)
713 {
714         free(G.rnfr_filename);
715         G.rnfr_filename = xstrdup(G.ftp_arg);
716         cmdio_write_ok(FTP_RNFROK);
717 }
718
719 static void
720 handle_rnto(void)
721 {
722         int retval;
723
724         /* If we didn't get a RNFR, throw a wobbly */
725         if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
726                 cmdio_write_raw(STR(FTP_NEEDRNFR)" RNFR required first\r\n");
727                 return;
728         }
729
730         retval = rename(G.rnfr_filename, G.ftp_arg);
731         free(G.rnfr_filename);
732         G.rnfr_filename = NULL;
733
734         if (retval) {
735                 cmdio_write_error(FTP_FILEFAIL);
736                 return;
737         }
738         cmdio_write_ok(FTP_RENAMEOK);
739 }
740
741 static void
742 handle_upload_common(int is_append, int is_unique)
743 {
744         struct stat statbuf;
745         char *tempname;
746         off_t bytes_transferred;
747         off_t offset;
748         int local_file_fd;
749         int remote_fd;
750
751         offset = G.restart_pos;
752         G.restart_pos = 0;
753
754         if (!port_or_pasv_was_seen())
755                 return; /* port_or_pasv_was_seen emitted error response */
756
757         tempname = NULL;
758         local_file_fd = -1;
759         if (is_unique) {
760                 tempname = xstrdup(" FILE: uniq.XXXXXX");
761                 local_file_fd = mkstemp(tempname + 7);
762         } else if (G.ftp_arg) {
763                 int flags = O_WRONLY | O_CREAT | O_TRUNC;
764                 if (is_append)
765                         flags = O_WRONLY | O_CREAT | O_APPEND;
766                 if (offset)
767                         flags = O_WRONLY | O_CREAT;
768                 local_file_fd = open(G.ftp_arg, flags, 0666);
769         }
770
771         if (local_file_fd < 0
772          || fstat(local_file_fd, &statbuf) != 0
773          || !S_ISREG(statbuf.st_mode)
774         ) {
775                 cmdio_write_error(FTP_UPLOADFAIL);
776                 if (local_file_fd >= 0)
777                         goto close_local_and_bail;
778                 return;
779         }
780         G.local_file_fd = local_file_fd;
781
782         if (offset)
783                 xlseek(local_file_fd, offset, SEEK_SET);
784
785         remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
786         free(tempname);
787
788         if (remote_fd < 0)
789                 goto close_local_and_bail;
790
791         bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
792         close(remote_fd);
793         if (bytes_transferred < 0)
794                 cmdio_write_error(FTP_BADSENDFILE);
795         else
796                 cmdio_write_ok(FTP_TRANSFEROK);
797
798  close_local_and_bail:
799         close(local_file_fd);
800         G.local_file_fd = 0;
801 }
802
803 static void
804 handle_stor(void)
805 {
806         handle_upload_common(0, 0);
807 }
808
809 static void
810 handle_appe(void)
811 {
812         G.restart_pos = 0;
813         handle_upload_common(1, 0);
814 }
815
816 static void
817 handle_stou(void)
818 {
819         G.restart_pos = 0;
820         handle_upload_common(0, 1);
821 }
822 #endif /* ENABLE_FEATURE_FTP_WRITE */
823
824 static uint32_t
825 cmdio_get_cmd_and_arg(void)
826 {
827         size_t len;
828         uint32_t cmdval;
829         char *cmd;
830
831         alarm(G.timeout);
832
833         free(G.ftp_cmd);
834         len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
835         G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len);
836         if (!cmd)
837                 exit(0);
838
839         /* Trailing '\n' is already stripped, strip '\r' */
840         len = strlen(cmd) - 1;
841         while ((ssize_t)len >= 0 && cmd[len] == '\r') {
842                 cmd[len] = '\0';
843                 len--;
844         }
845
846         G.ftp_arg = strchr(cmd, ' ');
847         if (G.ftp_arg != NULL)
848                 *G.ftp_arg++ = '\0';
849
850         /* Uppercase and pack into uint32_t first word of the command */
851         cmdval = 0;
852         while (*cmd)
853                 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
854
855         return cmdval;
856 }
857
858 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
859 #define mk_const3(a,b,c)    ((a * 0x100 + b) * 0x100 + c)
860 enum {
861         const_ALLO = mk_const4('A', 'L', 'L', 'O'),
862         const_APPE = mk_const4('A', 'P', 'P', 'E'),
863         const_CDUP = mk_const4('C', 'D', 'U', 'P'),
864         const_CWD  = mk_const3('C', 'W', 'D'),
865         const_DELE = mk_const4('D', 'E', 'L', 'E'),
866         const_EPSV = mk_const4('E', 'P', 'S', 'V'),
867         const_HELP = mk_const4('H', 'E', 'L', 'P'),
868         const_LIST = mk_const4('L', 'I', 'S', 'T'),
869         const_MKD  = mk_const3('M', 'K', 'D'),
870         const_MODE = mk_const4('M', 'O', 'D', 'E'),
871         const_NLST = mk_const4('N', 'L', 'S', 'T'),
872         const_NOOP = mk_const4('N', 'O', 'O', 'P'),
873         const_PASS = mk_const4('P', 'A', 'S', 'S'),
874         const_PASV = mk_const4('P', 'A', 'S', 'V'),
875         const_PORT = mk_const4('P', 'O', 'R', 'T'),
876         const_PWD  = mk_const3('P', 'W', 'D'),
877         const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
878         const_REST = mk_const4('R', 'E', 'S', 'T'),
879         const_RETR = mk_const4('R', 'E', 'T', 'R'),
880         const_RMD  = mk_const3('R', 'M', 'D'),
881         const_RNFR = mk_const4('R', 'N', 'F', 'R'),
882         const_RNTO = mk_const4('R', 'N', 'T', 'O'),
883         const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
884         const_STAT = mk_const4('S', 'T', 'A', 'T'),
885         const_STOR = mk_const4('S', 'T', 'O', 'R'),
886         const_STOU = mk_const4('S', 'T', 'O', 'U'),
887         const_STRU = mk_const4('S', 'T', 'R', 'U'),
888         const_SYST = mk_const4('S', 'Y', 'S', 'T'),
889         const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
890         const_USER = mk_const4('U', 'S', 'E', 'R'),
891
892         OPT_l = (1 << 0),
893         OPT_1 = (1 << 1),
894         OPT_v = (1 << 2),
895         OPT_S = (1 << 3),
896         OPT_w = (1 << 4),
897 };
898
899 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
900 int ftpd_main(int argc, char **argv)
901 {
902         smallint opts;
903
904         INIT_G();
905
906         G.start_time = monotonic_sec();
907         G.abs_timeout = 1 * 60 * 60;
908         G.timeout = 2 * 60;
909         opt_complementary = "t+:T+";
910         opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &G.abs_timeout);
911
912         if (opts & (OPT_l|OPT_1)) {
913                 /* Our secret backdoor to ls */
914                 memset(&G, 0, sizeof(G));
915 /* TODO: pass -n too? */
916                 xchdir(argv[2]);
917                 argv[2] = (char*)"--";
918                 return ls_main(argc, argv);
919         }
920
921
922         G.local_addr = get_sock_lsa(STDIN_FILENO);
923         if (!G.local_addr) {
924                 /* This is confusing:
925                  * bb_error_msg_and_die("stdin is not a socket");
926                  * Better: */
927                 bb_show_usage();
928                 /* Help text says that ftpd must be used as inetd service,
929                  * which is by far the most usual cause of get_sock_lsa
930                  * failure */
931         }
932
933         if (!(opts & OPT_v))
934                 logmode = LOGMODE_NONE;
935         if (opts & OPT_S) {
936                 /* LOG_NDELAY is needed since we may chroot later */
937                 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
938                 logmode |= LOGMODE_SYSLOG;
939         }
940
941         G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
942
943         if (argv[optind]) {
944                 xchdir(argv[optind]);
945                 chroot(".");
946         }
947
948         //umask(077); - admin can set umask before starting us
949
950         /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
951         signal(SIGPIPE, SIG_IGN);
952
953         /* Set up options on the command socket (do we need these all? why?) */
954         setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
955         setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
956         setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
957
958         cmdio_write_ok(FTP_GREET);
959         signal(SIGALRM, timeout_handler);
960
961 #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
962         {
963                 smallint user_was_specified = 0;
964                 while (1) {
965                         uint32_t cmdval = cmdio_get_cmd_and_arg();
966
967                         if (cmdval == const_USER) {
968                                 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
969                                         cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
970                                 else {
971                                         user_was_specified = 1;
972                                         cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
973                                 }
974                         } else if (cmdval == const_PASS) {
975                                 if (user_was_specified)
976                                         break;
977                                 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
978                         } else if (cmdval == const_QUIT) {
979                                 cmdio_write_ok(FTP_GOODBYE);
980                                 return 0;
981                         } else {
982                                 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
983                         }
984                 }
985         }
986         cmdio_write_ok(FTP_LOGINOK);
987 #endif
988
989         /* RFC-959 Section 5.1
990          * The following commands and options MUST be supported by every
991          * server-FTP and user-FTP, except in cases where the underlying
992          * file system or operating system does not allow or support
993          * a particular command.
994          * Type: ASCII Non-print, IMAGE, LOCAL 8
995          * Mode: Stream
996          * Structure: File, Record*
997          * (Record structure is REQUIRED only for hosts whose file
998          *  systems support record structure).
999          * Commands:
1000          * USER, PASS, ACCT, [bbox: ACCT not supported]
1001          * PORT, PASV,
1002          * TYPE, MODE, STRU,
1003          * RETR, STOR, APPE,
1004          * RNFR, RNTO, DELE,
1005          * CWD,  CDUP, RMD,  MKD,  PWD,
1006          * LIST, NLST,
1007          * SYST, STAT,
1008          * HELP, NOOP, QUIT.
1009          */
1010         /* ACCOUNT (ACCT)
1011          * "The argument field is a Telnet string identifying the user's account.
1012          * The command is not necessarily related to the USER command, as some
1013          * sites may require an account for login and others only for specific
1014          * access, such as storing files. In the latter case the command may
1015          * arrive at any time.
1016          * There are reply codes to differentiate these cases for the automation:
1017          * when account information is required for login, the response to
1018          * a successful PASSword command is reply code 332. On the other hand,
1019          * if account information is NOT required for login, the reply to
1020          * a successful PASSword command is 230; and if the account information
1021          * is needed for a command issued later in the dialogue, the server
1022          * should return a 332 or 532 reply depending on whether it stores
1023          * (pending receipt of the ACCounT command) or discards the command,
1024          * respectively."
1025          */
1026
1027         while (1) {
1028                 uint32_t cmdval = cmdio_get_cmd_and_arg();
1029
1030                 if (cmdval == const_QUIT) {
1031                         cmdio_write_ok(FTP_GOODBYE);
1032                         return 0;
1033                 }
1034                 else if (cmdval == const_USER)
1035                         cmdio_write_ok(FTP_GIVEPWORD);
1036                 else if (cmdval == const_PASS)
1037                         cmdio_write_ok(FTP_LOGINOK);
1038                 else if (cmdval == const_NOOP)
1039                         cmdio_write_ok(FTP_NOOPOK);
1040                 else if (cmdval == const_TYPE)
1041                         cmdio_write_ok(FTP_TYPEOK);
1042                 else if (cmdval == const_STRU)
1043                         cmdio_write_ok(FTP_STRUOK);
1044                 else if (cmdval == const_MODE)
1045                         cmdio_write_ok(FTP_MODEOK);
1046                 else if (cmdval == const_ALLO)
1047                         cmdio_write_ok(FTP_ALLOOK);
1048                 else if (cmdval == const_SYST)
1049                         cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1050                 else if (cmdval == const_PWD)
1051                         handle_pwd();
1052                 else if (cmdval == const_CWD)
1053                         handle_cwd();
1054                 else if (cmdval == const_CDUP) /* cd .. */
1055                         handle_cdup();
1056                 else if (cmdval == const_HELP)
1057                         handle_help();
1058                 else if (cmdval == const_LIST) /* ls -l */
1059                         handle_list();
1060                 else if (cmdval == const_NLST) /* "name list", bare ls */
1061                         handle_nlst();
1062                 else if (cmdval == const_SIZE)
1063                         handle_size();
1064                 else if (cmdval == const_STAT) {
1065                         if (G.ftp_arg == NULL)
1066                                 handle_stat();
1067                         else
1068                                 handle_stat_file();
1069                 }
1070                 else if (cmdval == const_PASV)
1071                         handle_pasv();
1072                 else if (cmdval == const_EPSV)
1073                         handle_epsv();
1074                 else if (cmdval == const_RETR)
1075                         handle_retr();
1076                 else if (cmdval == const_PORT)
1077                         handle_port();
1078                 else if (cmdval == const_REST)
1079                         handle_rest();
1080 #if ENABLE_FEATURE_FTP_WRITE
1081                 else if (opts & OPT_w) {
1082                         if (cmdval == const_STOR)
1083                                 handle_stor();
1084                         else if (cmdval == const_MKD)
1085                                 handle_mkd();
1086                         else if (cmdval == const_RMD)
1087                                 handle_rmd();
1088                         else if (cmdval == const_DELE)
1089                                 handle_dele();
1090                         else if (cmdval == const_RNFR) /* "rename from" */
1091                                 handle_rnfr();
1092                         else if (cmdval == const_RNTO) /* "rename to" */
1093                                 handle_rnto();
1094                         else if (cmdval == const_APPE)
1095                                 handle_appe();
1096                         else if (cmdval == const_STOU) /* "store unique" */
1097                                 handle_stou();
1098                 }
1099 #endif
1100 #if 0
1101                 else if (cmdval == const_STOR
1102                  || cmdval == const_MKD
1103                  || cmdval == const_RMD
1104                  || cmdval == const_DELE
1105                  || cmdval == const_RNFR
1106                  || cmdval == const_RNTO
1107                  || cmdval == const_APPE
1108                  || cmdval == const_STOU
1109                 ) {
1110                         cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1111                 }
1112 #endif
1113                 else {
1114                         /* Which unsupported commands were seen in the wild?
1115                          * (doesn't necessarily mean "we must support them")
1116                          * lftp 3.6.3: FEAT - is it useful?
1117                          *             MDTM - works fine without it anyway
1118                          */
1119                         cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
1120                 }
1121         }
1122 }