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