ftpd.c revision 1.29 1 /* $NetBSD: ftpd.c,v 1.29 1997/07/21 05:13:10 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT(
39 "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
40 The Regents of the University of California. All rights reserved.\n");
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
46 #else
47 __RCSID("$NetBSD: ftpd.c,v 1.29 1997/07/21 05:13:10 mrg Exp $");
48 #endif
49 #endif /* not lint */
50
51 /*
52 * FTP server.
53 */
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/wait.h>
59
60 #include <netinet/in.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63
64 #define FTP_NAMES
65 #include <arpa/ftp.h>
66 #include <arpa/inet.h>
67 #include <arpa/telnet.h>
68
69 #include <ctype.h>
70 #include <dirent.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <fnmatch.h>
75 #include <glob.h>
76 #include <limits.h>
77 #include <netdb.h>
78 #include <pwd.h>
79 #include <setjmp.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <syslog.h>
85 #include <time.h>
86 #include <unistd.h>
87 #ifdef SKEY
88 #include <skey.h>
89 #endif
90
91 #include "extern.h"
92 #include "pathnames.h"
93
94 #if __STDC__
95 #include <stdarg.h>
96 #else
97 #include <varargs.h>
98 #endif
99
100 static char version[] = "Version 7.00";
101
102 extern off_t restart_point;
103 extern char cbuf[];
104 extern int yyparse __P((void));
105
106 struct sockaddr_in ctrl_addr;
107 struct sockaddr_in data_source;
108 struct sockaddr_in data_dest;
109 struct sockaddr_in his_addr;
110 struct sockaddr_in pasv_addr;
111
112 int data;
113 jmp_buf errcatch, urgcatch;
114 int logged_in;
115 struct passwd *pw;
116 int debug;
117 int logging;
118 int guest;
119 int dochroot;
120 int type;
121 int form;
122 int stru; /* avoid C keyword */
123 int mode;
124 int usedefault = 1; /* for data transfers */
125 int pdata = -1; /* for passive mode */
126 sig_atomic_t transflag;
127 off_t file_size;
128 off_t byte_count;
129 char tmpline[7];
130 char hostname[MAXHOSTNAMELEN];
131 char remotehost[MAXHOSTNAMELEN];
132 static char ttyline[20];
133 char *tty = ttyline; /* for klogin */
134 static char *anondir = NULL;
135
136 extern struct ftpclass curclass;
137
138 #if defined(KERBEROS)
139 int notickets = 1;
140 char *krbtkfile_env = NULL;
141 #endif
142
143 /*
144 * Timeout intervals for retrying connections
145 * to hosts that don't accept PORT cmds. This
146 * is a kludge, but given the problems with TCP...
147 */
148 #define SWAITMAX 90 /* wait at most 90 seconds */
149 #define SWAITINT 5 /* interval between retries */
150
151 int swaitmax = SWAITMAX;
152 int swaitint = SWAITINT;
153
154 #ifdef HASSETPROCTITLE
155 char proctitle[BUFSIZ]; /* initial part of title */
156 #endif /* HASSETPROCTITLE */
157
158 #define LOGCMD(cmd, file) \
159 if (logging > 1) \
160 syslog(LOG_INFO,"%s %s%s", cmd, \
161 *(file) == '/' ? "" : curdir(), file);
162 #define LOGCMD2(cmd, file1, file2) \
163 if (logging > 1) \
164 syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
165 *(file1) == '/' ? "" : curdir(), file1, \
166 *(file2) == '/' ? "" : curdir(), file2);
167 #define LOGBYTES(cmd, file, cnt) \
168 if (logging > 1) { \
169 if (cnt == (off_t)-1) \
170 syslog(LOG_INFO,"%s %s%s", cmd, \
171 *(file) == '/' ? "" : curdir(), file); \
172 else \
173 syslog(LOG_INFO, "%s %s%s = %qd bytes", \
174 cmd, (*(file) == '/') ? "" : curdir(), file, \
175 (long long)cnt); \
176 }
177
178 static void ack __P((char *));
179 static void myoob __P((int));
180 static int checkuser __P((char *, char *));
181 static int checkaccess __P((char *));
182 static FILE *dataconn __P((char *, off_t, char *));
183 static void dolog __P((struct sockaddr_in *));
184 static char *curdir __P((void));
185 static void end_login __P((void));
186 static FILE *getdatasock __P((char *));
187 static char *gunique __P((char *));
188 static void lostconn __P((int));
189 static int receive_data __P((FILE *, FILE *));
190 static void send_data __P((FILE *, FILE *, off_t));
191 static struct passwd *
192 sgetpwnam __P((char *));
193 static char *sgetsave __P((char *));
194
195 int main __P((int, char *[], char **));
196
197 #if defined (KERBEROS)
198 int klogin __P((struct passwd *, char *, char *, char *));
199 #endif
200
201 static char *
202 curdir()
203 {
204 static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */
205
206 if (getcwd(path, sizeof(path)-2) == NULL)
207 return ("");
208 if (path[1] != '\0') /* special case for root dir. */
209 strcat(path, "/");
210 /* For guest account, skip / since it's chrooted */
211 return (guest ? path+1 : path);
212 }
213
214 int
215 main(argc, argv, envp)
216 int argc;
217 char *argv[];
218 char **envp;
219 {
220 int addrlen, ch, on = 1, tos;
221 char *cp, line[LINE_MAX];
222 FILE *fd;
223
224 /*
225 * LOG_NDELAY sets up the logging connection immediately,
226 * necessary for anonymous ftp's that chroot and can't do it later.
227 */
228 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
229 addrlen = sizeof(his_addr);
230 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
231 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
232 exit(1);
233 }
234 addrlen = sizeof(ctrl_addr);
235 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
236 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
237 exit(1);
238 }
239 #ifdef IP_TOS
240 tos = IPTOS_LOWDELAY;
241 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
242 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
243 #endif
244 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
245 debug = 0;
246
247 /* set this here so klogin can use it... */
248 (void)snprintf(ttyline, sizeof ttyline, "ftp%d", getpid());
249
250 while ((ch = getopt(argc, argv, "a:dlt:T:u:v")) != EOF) {
251 switch (ch) {
252 case 'a':
253 anondir = optarg;
254 break;
255
256 case 'd':
257 case 'v': /* deprecated */
258 debug = 1;
259 break;
260
261 case 'l':
262 logging++; /* > 1 == extra logging */
263 break;
264
265 case 't':
266 case 'T':
267 case 'u':
268 warnx("-%c has be deprecated in favour of ftpd.conf",
269 ch);
270 break;
271
272 default:
273 warnx("unknown flag -%c ignored", optopt);
274 break;
275 }
276 }
277 (void) freopen(_PATH_DEVNULL, "w", stderr);
278 (void) signal(SIGPIPE, lostconn);
279 (void) signal(SIGCHLD, SIG_IGN);
280 if ((long)signal(SIGURG, myoob) < 0)
281 syslog(LOG_ERR, "signal: %m");
282
283 /* Try to handle urgent data inline */
284 #ifdef SO_OOBINLINE
285 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
286 syslog(LOG_ERR, "setsockopt: %m");
287 #endif
288
289 #ifdef F_SETOWN
290 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
291 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
292 #endif
293 dolog(&his_addr);
294 /*
295 * Set up default state
296 */
297 data = -1;
298 type = TYPE_A;
299 form = FORM_N;
300 stru = STRU_F;
301 mode = MODE_S;
302 tmpline[0] = '\0';
303
304 /* If logins are disabled, print out the message. */
305 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
306 while (fgets(line, sizeof(line), fd) != NULL) {
307 if ((cp = strchr(line, '\n')) != NULL)
308 *cp = '\0';
309 lreply(530, "%s", line);
310 }
311 (void) fflush(stdout);
312 (void) fclose(fd);
313 reply(530, "System not available.");
314 exit(0);
315 }
316 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
317 while (fgets(line, sizeof(line), fd) != NULL) {
318 if ((cp = strchr(line, '\n')) != NULL)
319 *cp = '\0';
320 lreply(220, "%s", line);
321 }
322 (void) fflush(stdout);
323 (void) fclose(fd);
324 /* reply(220,) must follow */
325 }
326 (void) gethostname(hostname, sizeof(hostname));
327 reply(220, "%s FTP server (%s) ready.", hostname, version);
328 (void) setjmp(errcatch);
329 for (;;)
330 (void) yyparse();
331 /* NOTREACHED */
332 }
333
334 static void
335 lostconn(signo)
336 int signo;
337 {
338
339 if (debug)
340 syslog(LOG_DEBUG, "lost connection");
341 dologout(-1);
342 }
343
344 /*
345 * Helper function for sgetpwnam().
346 */
347 static char *
348 sgetsave(s)
349 char *s;
350 {
351 char *new = malloc((unsigned) strlen(s) + 1);
352
353 if (new == NULL) {
354 perror_reply(421, "Local resource failure: malloc");
355 dologout(1);
356 /* NOTREACHED */
357 }
358 (void) strcpy(new, s);
359 return (new);
360 }
361
362 /*
363 * Save the result of a getpwnam. Used for USER command, since
364 * the data returned must not be clobbered by any other command
365 * (e.g., globbing).
366 */
367 static struct passwd *
368 sgetpwnam(name)
369 char *name;
370 {
371 static struct passwd save;
372 struct passwd *p;
373
374 if ((p = getpwnam(name)) == NULL)
375 return (p);
376 if (save.pw_name) {
377 free(save.pw_name);
378 free(save.pw_passwd);
379 free(save.pw_gecos);
380 free(save.pw_dir);
381 free(save.pw_shell);
382 }
383 save = *p;
384 save.pw_name = sgetsave(p->pw_name);
385 save.pw_passwd = sgetsave(p->pw_passwd);
386 save.pw_gecos = sgetsave(p->pw_gecos);
387 save.pw_dir = sgetsave(p->pw_dir);
388 save.pw_shell = sgetsave(p->pw_shell);
389 return (&save);
390 }
391
392 static int login_attempts; /* number of failed login attempts */
393 static int askpasswd; /* had user command, ask for passwd */
394 static char curname[10]; /* current USER name */
395
396 /*
397 * USER command.
398 * Sets global passwd pointer pw if named account exists and is acceptable;
399 * sets askpasswd if a PASS command is expected. If logged in previously,
400 * need to reset state. If name is "ftp" or "anonymous", the name is not in
401 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
403 * requesting login privileges. Disallow anyone who does not have a standard
404 * shell as returned by getusershell(). Disallow anyone mentioned in the file
405 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
406 */
407 void
408 user(name)
409 char *name;
410 {
411 if (logged_in) {
412 if (guest) {
413 reply(530, "Can't change user from guest login.");
414 return;
415 } else if (dochroot) {
416 reply(530, "Can't change user from chroot user.");
417 return;
418 }
419 end_login();
420 }
421
422 guest = 0;
423 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
424 if (checkaccess("ftp") || checkaccess("anonymous"))
425 reply(530, "User %s access denied.", name);
426 else if ((pw = sgetpwnam("ftp")) != NULL) {
427 guest = 1;
428 askpasswd = 1;
429 reply(331,
430 "Guest login ok, type your name as password.");
431 } else
432 reply(530, "User %s unknown.", name);
433 if (!askpasswd && logging)
434 syslog(LOG_NOTICE,
435 "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
436 return;
437 }
438 pw = sgetpwnam(name);
439 if (logging)
440 strncpy(curname, name, sizeof(curname)-1);
441 #ifdef SKEY
442 if (!skey_haskey(name)) {
443 char *myskey, *skey_keyinfo __P((char *name));
444
445 myskey = skey_keyinfo(name);
446 reply(331, "Password [%s] for %s required.",
447 myskey ? myskey : "error getting challenge", name);
448 } else
449 #endif
450 reply(331, "Password required for %s.", name);
451
452 askpasswd = 1;
453 /*
454 * Delay before reading passwd after first failed
455 * attempt to slow down passwd-guessing programs.
456 */
457 if (login_attempts)
458 sleep((unsigned) login_attempts);
459 }
460
461 /*
462 * Check if a user is in the file "fname"
463 */
464 static int
465 checkuser(fname, name)
466 char *fname;
467 char *name;
468 {
469 FILE *fd;
470 int found = 0;
471 char *p, line[BUFSIZ];
472
473 if ((fd = fopen(fname, "r")) != NULL) {
474 while (fgets(line, sizeof(line), fd) != NULL)
475 if ((p = strchr(line, '\n')) != NULL) {
476 *p = '\0';
477 if (line[0] == '#')
478 continue;
479 if (strcmp(line, name) == 0) {
480 found = 1;
481 break;
482 }
483 }
484 (void) fclose(fd);
485 }
486 return (found);
487 }
488
489 /*
490 * Determine whether a user has access, based on information in
491 * _PATH_FTPUSERS. Each line is a shell-style glob followed by
492 * `allow' or `deny' (with deny being the default if anything but
493 * `allow', or nothing at all, is specified).
494 *
495 * Each glob is matched against the username in turn, and the first
496 * match found is used. If no match is found, access is allowed.
497 *
498 * Any line starting with `#' is considered a comment and ignored.
499 *
500 * This is probably not the best way to do this, but it preserves
501 * the old semantics where if a user was listed in the file he was
502 * denied, otherwise he was allowed.
503 *
504 * There is one change in the semantics, however; ftpd will now `fail
505 * safe' and deny all access if there's no /etc/ftpusers file.
506 *
507 * Return 1 if the user is denied, or 0 if he is allowed.
508 */
509 static int
510 checkaccess(name)
511 char *name;
512 {
513 #define ALLOWED 0
514 #define NOT_ALLOWED 1
515 FILE *fd;
516 int retval = ALLOWED;
517 char *glob, *perm, line[BUFSIZ];
518
519 if ((fd = fopen(_PATH_FTPUSERS, "r")) == NULL)
520 return NOT_ALLOWED;
521
522 while (fgets(line, sizeof(line), fd) != NULL) {
523 glob = strtok(line, " \t\n");
524 if (glob[0] == '#')
525 continue;
526 perm = strtok(NULL, " \t\n");
527 if (fnmatch(glob, name, 0) == 0) {
528 if (perm != NULL && strcmp(perm, "allow") == 0)
529 retval = ALLOWED;
530 else
531 retval = NOT_ALLOWED;
532 break;
533 }
534 }
535 (void) fclose(fd);
536 return (retval);
537
538 }
539 #undef ALLOWED
540 #undef NOT_ALLOWED
541
542 /*
543 * Terminate login as previous user, if any, resetting state;
544 * used when USER command is given or login fails.
545 */
546 static void
547 end_login()
548 {
549
550 (void) seteuid((uid_t)0);
551 if (logged_in)
552 logwtmp(ttyline, "", "");
553 pw = NULL;
554 logged_in = 0;
555 guest = 0;
556 dochroot = 0;
557 }
558
559 void
560 pass(passwd)
561 char *passwd;
562 {
563 int rval;
564 FILE *fd;
565 char *cp, *shell;
566
567 if (logged_in || askpasswd == 0) {
568 reply(503, "Login with USER first.");
569 return;
570 }
571 askpasswd = 0;
572 if (!guest) { /* "ftp" is only account allowed no password */
573 if (pw == NULL) {
574 rval = 1; /* failure below */
575 goto skip;
576 }
577 #if defined(KERBEROS)
578 rval = klogin(pw, "", hostname, passwd);
579 if (rval == 0)
580 goto skip;
581 #endif
582 #ifdef SKEY
583 if (skey_haskey(pw->pw_name) == 0 &&
584 (skey_passcheck(pw->pw_name, passwd) != -1)) {
585 rval = 0;
586 goto skip;
587 }
588 #endif
589 /* the strcmp does not catch null passwords! */
590 if (pw == NULL || *pw->pw_passwd == '\0' ||
591 strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) {
592 rval = 1; /* failure */
593 goto skip;
594 }
595 rval = 0;
596
597 skip:
598 /*
599 * If rval == 1, the user failed the authentication check
600 * above. If rval == 0, either Kerberos or local authentication
601 * succeeded.
602 */
603 if (rval) {
604 reply(530, "Login incorrect.");
605 if (logging) {
606 syslog(LOG_NOTICE,
607 "FTP LOGIN FAILED FROM %s", remotehost);
608 syslog(LOG_AUTHPRIV | LOG_NOTICE,
609 "FTP LOGIN FAILED FROM %s, %s",
610 remotehost, curname);
611 }
612 pw = NULL;
613 if (login_attempts++ >= 5) {
614 syslog(LOG_NOTICE,
615 "repeated login failures from %s",
616 remotehost);
617 exit(0);
618 }
619 return;
620 }
621 }
622
623 /* password was ok; see if anything else prevents login */
624 if (checkaccess(pw->pw_name)) {
625 reply(530, "User %s may not use FTP.", pw->pw_name);
626 if (logging)
627 syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
628 remotehost, pw->pw_name);
629 pw = (struct passwd *) NULL;
630 return;
631 }
632 /* check for valid shell, if not guest user */
633 if ((shell = pw->pw_shell) == NULL || *shell == 0)
634 shell = _PATH_BSHELL;
635 while ((cp = getusershell()) != NULL)
636 if (strcmp(cp, shell) == 0)
637 break;
638 endusershell();
639 if (cp == NULL && guest == 0) {
640 reply(530, "User %s may not use FTP.", pw->pw_name);
641 if (logging)
642 syslog(LOG_NOTICE,
643 "FTP LOGIN REFUSED FROM %s, %s",
644 remotehost, pw->pw_name);
645 pw = (struct passwd *) NULL;
646 return;
647 }
648
649 login_attempts = 0; /* this time successful */
650 if (setegid((gid_t)pw->pw_gid) < 0) {
651 reply(550, "Can't set gid.");
652 return;
653 }
654 (void) initgroups(pw->pw_name, pw->pw_gid);
655
656 /* open wtmp before chroot */
657 logwtmp(ttyline, pw->pw_name, remotehost);
658 logged_in = 1;
659
660 dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
661
662 /* parse ftpd.conf, setting up various parameters */
663 if (guest)
664 parse_conf(CLASS_GUEST);
665 else if (dochroot)
666 parse_conf(CLASS_CHROOT);
667 else
668 parse_conf(CLASS_REAL);
669
670 if (guest) {
671 /*
672 * We MUST do a chdir() after the chroot. Otherwise
673 * the old current directory will be accessible as "."
674 * outside the new root!
675 */
676 if (chroot(anondir ? anondir : pw->pw_dir) < 0 ||
677 chdir("/") < 0) {
678 reply(550, "Can't set guest privileges.");
679 goto bad;
680 }
681 } else if (dochroot) {
682 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
683 reply(550, "Can't change root.");
684 goto bad;
685 }
686 } else if (chdir(pw->pw_dir) < 0) {
687 if (chdir("/") < 0) {
688 reply(530, "User %s: can't change directory to %s.",
689 pw->pw_name, pw->pw_dir);
690 goto bad;
691 } else
692 lreply(230, "No directory! Logging in with home=/");
693 }
694 if (seteuid((uid_t)pw->pw_uid) < 0) {
695 reply(550, "Can't set uid.");
696 goto bad;
697 }
698 /*
699 * Display a login message, if it exists.
700 * N.B. reply(230,) must follow the message.
701 */
702 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
703 char *cp, line[LINE_MAX];
704
705 while (fgets(line, sizeof(line), fd) != NULL) {
706 if ((cp = strchr(line, '\n')) != NULL)
707 *cp = '\0';
708 lreply(230, "%s", line);
709 }
710 (void) fflush(stdout);
711 (void) fclose(fd);
712 }
713 show_chdir_messages(230);
714 if (guest) {
715 reply(230, "Guest login ok, access restrictions apply.");
716 #ifdef HASSETPROCTITLE
717 snprintf(proctitle, sizeof(proctitle),
718 "%s: anonymous/%.*s", remotehost,
719 (int) (sizeof(proctitle) - sizeof(remotehost) -
720 sizeof(": anonymous/")), passwd);
721 setproctitle(proctitle);
722 #endif /* HASSETPROCTITLE */
723 if (logging)
724 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
725 remotehost, passwd);
726 } else {
727 reply(230, "User %s logged in.", pw->pw_name);
728 #ifdef HASSETPROCTITLE
729 snprintf(proctitle, sizeof(proctitle),
730 "%s: %s", remotehost, pw->pw_name);
731 setproctitle(proctitle);
732 #endif /* HASSETPROCTITLE */
733 if (logging)
734 syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
735 remotehost, pw->pw_name);
736 }
737 (void) umask(curclass.umask);
738 return;
739 bad:
740 /* Forget all about it... */
741 end_login();
742 }
743
744 void
745 retrieve(cmd, name)
746 char *cmd, *name;
747 {
748 FILE *fin = NULL, *dout;
749 struct stat st;
750 int (*closefunc) __P((FILE *)) = NULL;
751 int log;
752
753 log = (cmd == 0);
754 if (cmd == 0) {
755 fin = fopen(name, "r"), closefunc = fclose;
756 if (fin == NULL)
757 cmd = do_conversion(name);
758 }
759 if (cmd) {
760 char line[BUFSIZ];
761
762 (void)snprintf(line, sizeof line, cmd, name), name = line;
763 fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
764 st.st_size = -1;
765 st.st_blksize = BUFSIZ;
766 }
767 if (fin == NULL) {
768 if (errno != 0) {
769 perror_reply(550, name);
770 if (log) {
771 LOGCMD("get", name);
772 }
773 }
774 return;
775 }
776 byte_count = -1;
777 if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
778 reply(550, "%s: not a plain file.", name);
779 goto done;
780 }
781 if (restart_point) {
782 if (type == TYPE_A) {
783 off_t i, n;
784 int c;
785
786 n = restart_point;
787 i = 0;
788 while (i++ < n) {
789 if ((c=getc(fin)) == EOF) {
790 perror_reply(550, name);
791 goto done;
792 }
793 if (c == '\n')
794 i++;
795 }
796 } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
797 perror_reply(550, name);
798 goto done;
799 }
800 }
801 dout = dataconn(name, st.st_size, "w");
802 if (dout == NULL)
803 goto done;
804 send_data(fin, dout, st.st_blksize);
805 (void) fclose(dout);
806 data = -1;
807 pdata = -1;
808 done:
809 if (log)
810 LOGBYTES("get", name, byte_count);
811 (*closefunc)(fin);
812 }
813
814 void
815 store(name, mode, unique)
816 char *name, *mode;
817 int unique;
818 {
819 FILE *fout, *din;
820 struct stat st;
821 int (*closefunc) __P((FILE *));
822
823 if (unique && stat(name, &st) == 0 &&
824 (name = gunique(name)) == NULL) {
825 LOGCMD(*mode == 'w' ? "put" : "append", name);
826 return;
827 }
828
829 if (restart_point)
830 mode = "r+";
831 fout = fopen(name, mode);
832 closefunc = fclose;
833 if (fout == NULL) {
834 perror_reply(553, name);
835 LOGCMD(*mode == 'w' ? "put" : "append", name);
836 return;
837 }
838 byte_count = -1;
839 if (restart_point) {
840 if (type == TYPE_A) {
841 off_t i, n;
842 int c;
843
844 n = restart_point;
845 i = 0;
846 while (i++ < n) {
847 if ((c=getc(fout)) == EOF) {
848 perror_reply(550, name);
849 goto done;
850 }
851 if (c == '\n')
852 i++;
853 }
854 /*
855 * We must do this seek to "current" position
856 * because we are changing from reading to
857 * writing.
858 */
859 if (fseek(fout, 0L, SEEK_CUR) < 0) {
860 perror_reply(550, name);
861 goto done;
862 }
863 } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
864 perror_reply(550, name);
865 goto done;
866 }
867 }
868 din = dataconn(name, (off_t)-1, "r");
869 if (din == NULL)
870 goto done;
871 if (receive_data(din, fout) == 0) {
872 if (unique)
873 reply(226, "Transfer complete (unique file name:%s).",
874 name);
875 else
876 reply(226, "Transfer complete.");
877 }
878 (void) fclose(din);
879 data = -1;
880 pdata = -1;
881 done:
882 LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
883 (*closefunc)(fout);
884 }
885
886 static FILE *
887 getdatasock(mode)
888 char *mode;
889 {
890 int on = 1, s, t, tries;
891
892 if (data >= 0)
893 return (fdopen(data, mode));
894 (void) seteuid((uid_t)0);
895 s = socket(AF_INET, SOCK_STREAM, 0);
896 if (s < 0)
897 goto bad;
898 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
899 (char *) &on, sizeof(on)) < 0)
900 goto bad;
901 /* anchor socket to avoid multi-homing problems */
902 data_source.sin_len = sizeof(struct sockaddr_in);
903 data_source.sin_family = AF_INET;
904 data_source.sin_addr = ctrl_addr.sin_addr;
905 for (tries = 1; ; tries++) {
906 if (bind(s, (struct sockaddr *)&data_source,
907 sizeof(data_source)) >= 0)
908 break;
909 if (errno != EADDRINUSE || tries > 10)
910 goto bad;
911 sleep(tries);
912 }
913 (void) seteuid((uid_t)pw->pw_uid);
914 #ifdef IP_TOS
915 on = IPTOS_THROUGHPUT;
916 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
917 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
918 #endif
919 return (fdopen(s, mode));
920 bad:
921 /* Return the real value of errno (close may change it) */
922 t = errno;
923 (void) seteuid((uid_t)pw->pw_uid);
924 (void) close(s);
925 errno = t;
926 return (NULL);
927 }
928
929 static FILE *
930 dataconn(name, size, mode)
931 char *name;
932 off_t size;
933 char *mode;
934 {
935 char sizebuf[32];
936 FILE *file;
937 int retry = 0, tos;
938
939 file_size = size;
940 byte_count = 0;
941 if (size != (off_t) -1)
942 (void)snprintf(sizebuf, sizeof sizebuf, " (%qd bytes)",
943 (long long)size);
944 else
945 sizebuf[0] = '\0';
946 if (pdata >= 0) {
947 struct sockaddr_in from;
948 int s, fromlen = sizeof(from);
949
950 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
951 if (s < 0) {
952 reply(425, "Can't open data connection.");
953 (void) close(pdata);
954 pdata = -1;
955 return (NULL);
956 }
957 (void) close(pdata);
958 pdata = s;
959 #ifdef IP_TOS
960 tos = IPTOS_THROUGHPUT;
961 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
962 sizeof(int));
963 #endif
964 reply(150, "Opening %s mode data connection for '%s'%s.",
965 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
966 return (fdopen(pdata, mode));
967 }
968 if (data >= 0) {
969 reply(125, "Using existing data connection for '%s'%s.",
970 name, sizebuf);
971 usedefault = 1;
972 return (fdopen(data, mode));
973 }
974 if (usedefault)
975 data_dest = his_addr;
976 usedefault = 1;
977 file = getdatasock(mode);
978 if (file == NULL) {
979 reply(425, "Can't create data socket (%s,%d): %s.",
980 inet_ntoa(data_source.sin_addr),
981 ntohs(data_source.sin_port), strerror(errno));
982 return (NULL);
983 }
984 data = fileno(file);
985 while (connect(data, (struct sockaddr *)&data_dest,
986 sizeof(data_dest)) < 0) {
987 if (errno == EADDRINUSE && retry < swaitmax) {
988 sleep((unsigned) swaitint);
989 retry += swaitint;
990 continue;
991 }
992 perror_reply(425, "Can't build data connection");
993 (void) fclose(file);
994 data = -1;
995 return (NULL);
996 }
997 reply(150, "Opening %s mode data connection for '%s'%s.",
998 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
999 return (file);
1000 }
1001
1002 /*
1003 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1004 * encapsulation of the data subject * to Mode, Structure, and Type.
1005 *
1006 * NB: Form isn't handled.
1007 */
1008 static void
1009 send_data(instr, outstr, blksize)
1010 FILE *instr, *outstr;
1011 off_t blksize;
1012 {
1013 int c, cnt, filefd, netfd;
1014 char *buf;
1015
1016 transflag++;
1017 if (setjmp(urgcatch)) {
1018 transflag = 0;
1019 return;
1020 }
1021
1022 switch (type) {
1023
1024 case TYPE_A:
1025 while ((c = getc(instr)) != EOF) {
1026 byte_count++;
1027 if (c == '\n') {
1028 if (ferror(outstr))
1029 goto data_err;
1030 (void) putc('\r', outstr);
1031 }
1032 (void) putc(c, outstr);
1033 }
1034 fflush(outstr);
1035 transflag = 0;
1036 if (ferror(instr))
1037 goto file_err;
1038 if (ferror(outstr))
1039 goto data_err;
1040 reply(226, "Transfer complete.");
1041 return;
1042
1043 case TYPE_I:
1044 case TYPE_L:
1045 if ((buf = malloc((u_int)blksize)) == NULL) {
1046 transflag = 0;
1047 perror_reply(451, "Local resource failure: malloc");
1048 return;
1049 }
1050 netfd = fileno(outstr);
1051 filefd = fileno(instr);
1052 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1053 write(netfd, buf, cnt) == cnt)
1054 byte_count += cnt;
1055 transflag = 0;
1056 (void)free(buf);
1057 if (cnt != 0) {
1058 if (cnt < 0)
1059 goto file_err;
1060 goto data_err;
1061 }
1062 reply(226, "Transfer complete.");
1063 return;
1064 default:
1065 transflag = 0;
1066 reply(550, "Unimplemented TYPE %d in send_data", type);
1067 return;
1068 }
1069
1070 data_err:
1071 transflag = 0;
1072 perror_reply(426, "Data connection");
1073 return;
1074
1075 file_err:
1076 transflag = 0;
1077 perror_reply(551, "Error on input file");
1078 }
1079
1080 /*
1081 * Transfer data from peer to "outstr" using the appropriate encapulation of
1082 * the data subject to Mode, Structure, and Type.
1083 *
1084 * N.B.: Form isn't handled.
1085 */
1086 static int
1087 receive_data(instr, outstr)
1088 FILE *instr, *outstr;
1089 {
1090 int c, cnt, bare_lfs;
1091 char buf[BUFSIZ];
1092 #ifdef __GNUC__
1093 (void) &bare_lfs;
1094 #endif
1095
1096 bare_lfs = 0;
1097 transflag++;
1098 if (setjmp(urgcatch)) {
1099 transflag = 0;
1100 return (-1);
1101 }
1102
1103 switch (type) {
1104
1105 case TYPE_I:
1106 case TYPE_L:
1107 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1108 if (write(fileno(outstr), buf, cnt) != cnt)
1109 goto file_err;
1110 byte_count += cnt;
1111 }
1112 if (cnt < 0)
1113 goto data_err;
1114 transflag = 0;
1115 return (0);
1116
1117 case TYPE_E:
1118 reply(553, "TYPE E not implemented.");
1119 transflag = 0;
1120 return (-1);
1121
1122 case TYPE_A:
1123 while ((c = getc(instr)) != EOF) {
1124 byte_count++;
1125 if (c == '\n')
1126 bare_lfs++;
1127 while (c == '\r') {
1128 if (ferror(outstr))
1129 goto data_err;
1130 if ((c = getc(instr)) != '\n') {
1131 (void) putc ('\r', outstr);
1132 if (c == '\0' || c == EOF)
1133 goto contin2;
1134 }
1135 }
1136 (void) putc(c, outstr);
1137 contin2: ;
1138 }
1139 fflush(outstr);
1140 if (ferror(instr))
1141 goto data_err;
1142 if (ferror(outstr))
1143 goto file_err;
1144 transflag = 0;
1145 if (bare_lfs) {
1146 lreply(226,
1147 "WARNING! %d bare linefeeds received in ASCII mode",
1148 bare_lfs);
1149 (void)printf(" File may not have transferred correctly.\r\n");
1150 }
1151 return (0);
1152 default:
1153 reply(550, "Unimplemented TYPE %d in receive_data", type);
1154 transflag = 0;
1155 return (-1);
1156 }
1157
1158 data_err:
1159 transflag = 0;
1160 perror_reply(426, "Data Connection");
1161 return (-1);
1162
1163 file_err:
1164 transflag = 0;
1165 perror_reply(452, "Error writing file");
1166 return (-1);
1167 }
1168
1169 void
1170 statfilecmd(filename)
1171 char *filename;
1172 {
1173 FILE *fin;
1174 int c;
1175 char line[LINE_MAX];
1176
1177 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1178 fin = ftpd_popen(line, "r", 0);
1179 lreply(211, "status of %s:", filename);
1180 while ((c = getc(fin)) != EOF) {
1181 if (c == '\n') {
1182 if (ferror(stdout)){
1183 perror_reply(421, "control connection");
1184 (void) ftpd_pclose(fin);
1185 dologout(1);
1186 /* NOTREACHED */
1187 }
1188 if (ferror(fin)) {
1189 perror_reply(551, filename);
1190 (void) ftpd_pclose(fin);
1191 return;
1192 }
1193 (void) putc('\r', stdout);
1194 }
1195 (void) putc(c, stdout);
1196 }
1197 (void) ftpd_pclose(fin);
1198 reply(211, "End of Status");
1199 }
1200
1201 void
1202 statcmd()
1203 {
1204 struct sockaddr_in *sin;
1205 u_char *a, *p;
1206
1207 lreply(211, "%s FTP server status:", hostname);
1208 lreply(211, "%s", version);
1209 if (isdigit(remotehost[0]))
1210 lreply(211, "Connected to %s", remotehost);
1211 else
1212 lreply(211, "Connected to %s (%s)", remotehost,
1213 inet_ntoa(his_addr.sin_addr));
1214 if (logged_in) {
1215 if (guest)
1216 lreply(211, "Logged in anonymously");
1217 else
1218 lreply(211, "Logged in as %s", pw->pw_name);
1219 } else if (askpasswd)
1220 lreply(211, "Waiting for password");
1221 else
1222 lreply(211, "Waiting for user name");
1223 printf("211- TYPE: %s", typenames[type]);
1224 if (type == TYPE_A || type == TYPE_E)
1225 printf(", FORM: %s", formnames[form]);
1226 if (type == TYPE_L)
1227 #if NBBY == 8
1228 printf(" %d", NBBY);
1229 #else
1230 printf(" %d", bytesize); /* need definition! */
1231 #endif
1232 printf("; STRUcture: %s; transfer MODE: %s\r\n",
1233 strunames[stru], modenames[mode]);
1234 if (data != -1)
1235 lreply(211, "Data connection open");
1236 else if (pdata != -1) {
1237 printf("211- in Passive mode");
1238 sin = &pasv_addr;
1239 goto printaddr;
1240 } else if (usedefault == 0) {
1241 printf("211- PORT");
1242 sin = &data_dest;
1243 printaddr:
1244 a = (u_char *) &sin->sin_addr;
1245 p = (u_char *) &sin->sin_port;
1246 #define UC(b) (((int) b) & 0xff)
1247 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1248 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1249 #undef UC
1250 } else
1251 lreply(211, "No data connection");
1252
1253 if (logged_in) {
1254 struct ftpconv *cp;
1255
1256 lreply(211, "");
1257 lreply(211, "Class: %s", curclass.classname);
1258 if (curclass.display)
1259 lreply(211, "Display file: %s", curclass.display);
1260 if (curclass.notify)
1261 lreply(211, "Notify fileglob: %s", curclass.notify);
1262 lreply(211, "Idle timeout: %d, maximum timeout: %d",
1263 curclass.timeout, curclass.maxtimeout);
1264 lreply(211, "dele, mkd, rmd, umask, chmod: %sabled",
1265 curclass.modify ? "en" : "dis");
1266 lreply(211, "Umask: %.04o", curclass.umask);
1267 for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
1268 if (cp->suffix == NULL || cp->types == NULL ||
1269 cp->command == NULL)
1270 continue;
1271 lreply(211,
1272 "Conversion: %s [%s] disable: %s, command: %s",
1273 cp->suffix, cp->types, cp->disable, cp->command);
1274 }
1275 }
1276
1277 reply(211, "End of status");
1278 }
1279
1280 void
1281 fatal(s)
1282 char *s;
1283 {
1284
1285 reply(451, "Error in server: %s\n", s);
1286 reply(221, "Closing connection due to server error.");
1287 dologout(0);
1288 /* NOTREACHED */
1289 }
1290
1291 void
1292 #if __STDC__
1293 reply(int n, const char *fmt, ...)
1294 #else
1295 reply(n, fmt, va_alist)
1296 int n;
1297 char *fmt;
1298 va_dcl
1299 #endif
1300 {
1301 va_list ap;
1302 #if __STDC__
1303 va_start(ap, fmt);
1304 #else
1305 va_start(ap);
1306 #endif
1307 (void)printf("%d ", n);
1308 (void)vprintf(fmt, ap);
1309 (void)printf("\r\n");
1310 (void)fflush(stdout);
1311 if (debug) {
1312 syslog(LOG_DEBUG, "<--- %d ", n);
1313 vsyslog(LOG_DEBUG, fmt, ap);
1314 }
1315 }
1316
1317 void
1318 #if __STDC__
1319 lreply(int n, const char *fmt, ...)
1320 #else
1321 lreply(n, fmt, va_alist)
1322 int n;
1323 char *fmt;
1324 va_dcl
1325 #endif
1326 {
1327 va_list ap;
1328 #if __STDC__
1329 va_start(ap, fmt);
1330 #else
1331 va_start(ap);
1332 #endif
1333 (void)printf("%d- ", n);
1334 (void)vprintf(fmt, ap);
1335 (void)printf("\r\n");
1336 (void)fflush(stdout);
1337 if (debug) {
1338 syslog(LOG_DEBUG, "<--- %d- ", n);
1339 vsyslog(LOG_DEBUG, fmt, ap);
1340 }
1341 }
1342
1343 static void
1344 ack(s)
1345 char *s;
1346 {
1347
1348 reply(250, "%s command successful.", s);
1349 }
1350
1351 void
1352 nack(s)
1353 char *s;
1354 {
1355
1356 reply(502, "%s command not implemented.", s);
1357 }
1358
1359 /* ARGSUSED */
1360 void
1361 yyerror(s)
1362 char *s;
1363 {
1364 char *cp;
1365
1366 if ((cp = strchr(cbuf,'\n')) != NULL)
1367 *cp = '\0';
1368 reply(500, "'%s': command not understood.", cbuf);
1369 }
1370
1371 void
1372 delete(name)
1373 char *name;
1374 {
1375 struct stat st;
1376
1377 LOGCMD("delete", name);
1378 if (stat(name, &st) < 0) {
1379 perror_reply(550, name);
1380 return;
1381 }
1382 if ((st.st_mode&S_IFMT) == S_IFDIR) {
1383 if (rmdir(name) < 0) {
1384 perror_reply(550, name);
1385 return;
1386 }
1387 goto done;
1388 }
1389 if (unlink(name) < 0) {
1390 perror_reply(550, name);
1391 return;
1392 }
1393 done:
1394 ack("DELE");
1395 }
1396
1397 void
1398 cwd(path)
1399 char *path;
1400 {
1401
1402 if (chdir(path) < 0)
1403 perror_reply(550, path);
1404 else {
1405 show_chdir_messages(250);
1406 ack("CWD");
1407 }
1408 }
1409
1410 void
1411 makedir(name)
1412 char *name;
1413 {
1414
1415 LOGCMD("mkdir", name);
1416 if (mkdir(name, 0777) < 0)
1417 perror_reply(550, name);
1418 else
1419 reply(257, "MKD command successful.");
1420 }
1421
1422 void
1423 removedir(name)
1424 char *name;
1425 {
1426
1427 LOGCMD("rmdir", name);
1428 if (rmdir(name) < 0)
1429 perror_reply(550, name);
1430 else
1431 ack("RMD");
1432 }
1433
1434 void
1435 pwd()
1436 {
1437 char path[MAXPATHLEN + 1];
1438
1439 if (getwd(path) == (char *)NULL)
1440 reply(550, "%s.", path);
1441 else
1442 reply(257, "\"%s\" is current directory.", path);
1443 }
1444
1445 char *
1446 renamefrom(name)
1447 char *name;
1448 {
1449 struct stat st;
1450
1451 if (stat(name, &st) < 0) {
1452 perror_reply(550, name);
1453 return ((char *)0);
1454 }
1455 reply(350, "File exists, ready for destination name");
1456 return (name);
1457 }
1458
1459 void
1460 renamecmd(from, to)
1461 char *from, *to;
1462 {
1463
1464 LOGCMD2("rename", from, to);
1465 if (rename(from, to) < 0)
1466 perror_reply(550, "rename");
1467 else
1468 ack("RNTO");
1469 }
1470
1471 static void
1472 dolog(sin)
1473 struct sockaddr_in *sin;
1474 {
1475 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1476 sizeof(struct in_addr), AF_INET);
1477
1478 if (hp)
1479 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1480 else
1481 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1482 sizeof(remotehost));
1483 #ifdef HASSETPROCTITLE
1484 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1485 setproctitle(proctitle);
1486 #endif /* HASSETPROCTITLE */
1487
1488 if (logging)
1489 syslog(LOG_INFO, "connection from %s", remotehost);
1490 }
1491
1492 /*
1493 * Record logout in wtmp file
1494 * and exit with supplied status.
1495 */
1496 void
1497 dologout(status)
1498 int status;
1499 {
1500 /*
1501 * Prevent reception of SIGURG from resulting in a resumption
1502 * back to the main program loop.
1503 */
1504 transflag = 0;
1505
1506 if (logged_in) {
1507 (void) seteuid((uid_t)0);
1508 logwtmp(ttyline, "", "");
1509 #if defined(KERBEROS)
1510 if (!notickets && krbtkfile_env)
1511 unlink(krbtkfile_env);
1512 #endif
1513 }
1514 /* beware of flushing buffers after a SIGPIPE */
1515 _exit(status);
1516 }
1517
1518 static void
1519 myoob(signo)
1520 int signo;
1521 {
1522 char *cp;
1523
1524 /* only process if transfer occurring */
1525 if (!transflag)
1526 return;
1527 cp = tmpline;
1528 if (getline(cp, 7, stdin) == NULL) {
1529 reply(221, "You could at least say goodbye.");
1530 dologout(0);
1531 }
1532 upper(cp);
1533 if (strcmp(cp, "ABOR\r\n") == 0) {
1534 tmpline[0] = '\0';
1535 reply(426, "Transfer aborted. Data connection closed.");
1536 reply(226, "Abort successful");
1537 longjmp(urgcatch, 1);
1538 }
1539 if (strcmp(cp, "STAT\r\n") == 0) {
1540 if (file_size != (off_t) -1)
1541 reply(213, "Status: %qd of %qd bytes transferred",
1542 byte_count, file_size);
1543 else
1544 reply(213, "Status: %qd bytes transferred", byte_count);
1545 }
1546 }
1547
1548 /*
1549 * Note: a response of 425 is not mentioned as a possible response to
1550 * the PASV command in RFC959. However, it has been blessed as
1551 * a legitimate response by Jon Postel in a telephone conversation
1552 * with Rick Adams on 25 Jan 89.
1553 */
1554 void
1555 passive()
1556 {
1557 int len;
1558 char *p, *a;
1559
1560 pdata = socket(AF_INET, SOCK_STREAM, 0);
1561 if (pdata < 0 || !logged_in) {
1562 perror_reply(425, "Can't open passive connection");
1563 return;
1564 }
1565 pasv_addr = ctrl_addr;
1566 pasv_addr.sin_port = 0;
1567 (void) seteuid((uid_t)0);
1568 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1569 (void) seteuid((uid_t)pw->pw_uid);
1570 goto pasv_error;
1571 }
1572 (void) seteuid((uid_t)pw->pw_uid);
1573 len = sizeof(pasv_addr);
1574 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1575 goto pasv_error;
1576 if (listen(pdata, 1) < 0)
1577 goto pasv_error;
1578 a = (char *) &pasv_addr.sin_addr;
1579 p = (char *) &pasv_addr.sin_port;
1580
1581 #define UC(b) (((int) b) & 0xff)
1582
1583 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1584 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1585 return;
1586
1587 pasv_error:
1588 (void) close(pdata);
1589 pdata = -1;
1590 perror_reply(425, "Can't open passive connection");
1591 return;
1592 }
1593
1594 /*
1595 * Generate unique name for file with basename "local".
1596 * The file named "local" is already known to exist.
1597 * Generates failure reply on error.
1598 *
1599 * XXX this function should under go changes similar to
1600 * the mktemp(3)/mkstemp(3) changes.
1601 */
1602 static char *
1603 gunique(local)
1604 char *local;
1605 {
1606 static char new[MAXPATHLEN];
1607 struct stat st;
1608 int count, len;
1609 char *cp;
1610
1611 cp = strrchr(local, '/');
1612 if (cp)
1613 *cp = '\0';
1614 if (stat(cp ? local : ".", &st) < 0) {
1615 perror_reply(553, cp ? local : ".");
1616 return ((char *) 0);
1617 }
1618 if (cp)
1619 *cp = '/';
1620 (void) strcpy(new, local);
1621 len = strlen(new);
1622 cp = new + len;
1623 *cp++ = '.';
1624 for (count = 1; count < 100; count++) {
1625 (void)snprintf(cp, sizeof(new) - len - 2, "%d", count);
1626 if (stat(new, &st) < 0)
1627 return (new);
1628 }
1629 reply(452, "Unique file name cannot be created.");
1630 return (NULL);
1631 }
1632
1633 /*
1634 * Format and send reply containing system error number.
1635 */
1636 void
1637 perror_reply(code, string)
1638 int code;
1639 char *string;
1640 {
1641
1642 reply(code, "%s: %s.", string, strerror(errno));
1643 }
1644
1645 static char *onefile[] = {
1646 "",
1647 0
1648 };
1649
1650 void
1651 send_file_list(whichf)
1652 char *whichf;
1653 {
1654 struct stat st;
1655 DIR *dirp = NULL;
1656 struct dirent *dir;
1657 FILE *dout = NULL;
1658 char **dirlist, *dirname;
1659 int simple = 0;
1660 int freeglob = 0;
1661 glob_t gl;
1662 #ifdef __GNUC__
1663 (void) &dout;
1664 (void) &dirlist;
1665 (void) &simple;
1666 (void) &freeglob;
1667 #endif
1668
1669 if (strpbrk(whichf, "~{[*?") != NULL) {
1670 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1671
1672 memset(&gl, 0, sizeof(gl));
1673 freeglob = 1;
1674 if (glob(whichf, flags, 0, &gl)) {
1675 reply(550, "not found");
1676 goto out;
1677 } else if (gl.gl_pathc == 0) {
1678 errno = ENOENT;
1679 perror_reply(550, whichf);
1680 goto out;
1681 }
1682 dirlist = gl.gl_pathv;
1683 } else {
1684 onefile[0] = whichf;
1685 dirlist = onefile;
1686 simple = 1;
1687 }
1688
1689 if (setjmp(urgcatch)) {
1690 transflag = 0;
1691 goto out;
1692 }
1693 while ((dirname = *dirlist++) != NULL) {
1694 if (stat(dirname, &st) < 0) {
1695 /*
1696 * If user typed "ls -l", etc, and the client
1697 * used NLST, do what the user meant.
1698 */
1699 if (dirname[0] == '-' && *dirlist == NULL &&
1700 transflag == 0) {
1701 retrieve("/bin/ls %s", dirname);
1702 goto out;
1703 }
1704 perror_reply(550, whichf);
1705 if (dout != NULL) {
1706 (void) fclose(dout);
1707 transflag = 0;
1708 data = -1;
1709 pdata = -1;
1710 }
1711 goto out;
1712 }
1713
1714 if (S_ISREG(st.st_mode)) {
1715 if (dout == NULL) {
1716 dout = dataconn("file list", (off_t)-1, "w");
1717 if (dout == NULL)
1718 goto out;
1719 transflag++;
1720 }
1721 fprintf(dout, "%s%s\n", dirname,
1722 type == TYPE_A ? "\r" : "");
1723 byte_count += strlen(dirname) + 1;
1724 continue;
1725 } else if (!S_ISDIR(st.st_mode))
1726 continue;
1727
1728 if ((dirp = opendir(dirname)) == NULL)
1729 continue;
1730
1731 while ((dir = readdir(dirp)) != NULL) {
1732 char nbuf[MAXPATHLEN];
1733
1734 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1735 continue;
1736 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1737 dir->d_namlen == 2)
1738 continue;
1739
1740 (void)snprintf(nbuf, sizeof nbuf, "%s/%s", dirname,
1741 dir->d_name);
1742
1743 /*
1744 * We have to do a stat to insure it's
1745 * not a directory or special file.
1746 */
1747 if (simple || (stat(nbuf, &st) == 0 &&
1748 S_ISREG(st.st_mode))) {
1749 if (dout == NULL) {
1750 dout = dataconn("file list", (off_t)-1,
1751 "w");
1752 if (dout == NULL)
1753 goto out;
1754 transflag++;
1755 }
1756 if (nbuf[0] == '.' && nbuf[1] == '/')
1757 fprintf(dout, "%s%s\n", &nbuf[2],
1758 type == TYPE_A ? "\r" : "");
1759 else
1760 fprintf(dout, "%s%s\n", nbuf,
1761 type == TYPE_A ? "\r" : "");
1762 byte_count += strlen(nbuf) + 1;
1763 }
1764 }
1765 (void) closedir(dirp);
1766 }
1767
1768 if (dout == NULL)
1769 reply(550, "No files found.");
1770 else if (ferror(dout) != 0)
1771 perror_reply(550, "Data connection");
1772 else
1773 reply(226, "Transfer complete.");
1774
1775 transflag = 0;
1776 if (dout != NULL)
1777 (void) fclose(dout);
1778 data = -1;
1779 pdata = -1;
1780 out:
1781 if (freeglob) {
1782 freeglob = 0;
1783 globfree(&gl);
1784 }
1785 }
1786