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