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