ftpd.c revision 1.27 1 /* $NetBSD: ftpd.c,v 1.27 1997/06/21 18:01:35 cjs 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.27 1997/06/21 18:01:35 cjs 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)sprintf(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, *dout;
749 struct stat st;
750 int (*closefunc) __P((FILE *));
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) sprintf(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) sprintf(sizebuf, " (%qd bytes)", (long long)size);
943 else
944 (void) strcpy(sizebuf, "");
945 if (pdata >= 0) {
946 struct sockaddr_in from;
947 int s, fromlen = sizeof(from);
948
949 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
950 if (s < 0) {
951 reply(425, "Can't open data connection.");
952 (void) close(pdata);
953 pdata = -1;
954 return (NULL);
955 }
956 (void) close(pdata);
957 pdata = s;
958 #ifdef IP_TOS
959 tos = IPTOS_THROUGHPUT;
960 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
961 sizeof(int));
962 #endif
963 reply(150, "Opening %s mode data connection for '%s'%s.",
964 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
965 return (fdopen(pdata, mode));
966 }
967 if (data >= 0) {
968 reply(125, "Using existing data connection for '%s'%s.",
969 name, sizebuf);
970 usedefault = 1;
971 return (fdopen(data, mode));
972 }
973 if (usedefault)
974 data_dest = his_addr;
975 usedefault = 1;
976 file = getdatasock(mode);
977 if (file == NULL) {
978 reply(425, "Can't create data socket (%s,%d): %s.",
979 inet_ntoa(data_source.sin_addr),
980 ntohs(data_source.sin_port), strerror(errno));
981 return (NULL);
982 }
983 data = fileno(file);
984 while (connect(data, (struct sockaddr *)&data_dest,
985 sizeof(data_dest)) < 0) {
986 if (errno == EADDRINUSE && retry < swaitmax) {
987 sleep((unsigned) swaitint);
988 retry += swaitint;
989 continue;
990 }
991 perror_reply(425, "Can't build data connection");
992 (void) fclose(file);
993 data = -1;
994 return (NULL);
995 }
996 reply(150, "Opening %s mode data connection for '%s'%s.",
997 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
998 return (file);
999 }
1000
1001 /*
1002 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1003 * encapsulation of the data subject * to Mode, Structure, and Type.
1004 *
1005 * NB: Form isn't handled.
1006 */
1007 static void
1008 send_data(instr, outstr, blksize)
1009 FILE *instr, *outstr;
1010 off_t blksize;
1011 {
1012 int c, cnt, filefd, netfd;
1013 char *buf;
1014
1015 transflag++;
1016 if (setjmp(urgcatch)) {
1017 transflag = 0;
1018 return;
1019 }
1020
1021 switch (type) {
1022
1023 case TYPE_A:
1024 while ((c = getc(instr)) != EOF) {
1025 byte_count++;
1026 if (c == '\n') {
1027 if (ferror(outstr))
1028 goto data_err;
1029 (void) putc('\r', outstr);
1030 }
1031 (void) putc(c, outstr);
1032 }
1033 fflush(outstr);
1034 transflag = 0;
1035 if (ferror(instr))
1036 goto file_err;
1037 if (ferror(outstr))
1038 goto data_err;
1039 reply(226, "Transfer complete.");
1040 return;
1041
1042 case TYPE_I:
1043 case TYPE_L:
1044 if ((buf = malloc((u_int)blksize)) == NULL) {
1045 transflag = 0;
1046 perror_reply(451, "Local resource failure: malloc");
1047 return;
1048 }
1049 netfd = fileno(outstr);
1050 filefd = fileno(instr);
1051 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1052 write(netfd, buf, cnt) == cnt)
1053 byte_count += cnt;
1054 transflag = 0;
1055 (void)free(buf);
1056 if (cnt != 0) {
1057 if (cnt < 0)
1058 goto file_err;
1059 goto data_err;
1060 }
1061 reply(226, "Transfer complete.");
1062 return;
1063 default:
1064 transflag = 0;
1065 reply(550, "Unimplemented TYPE %d in send_data", type);
1066 return;
1067 }
1068
1069 data_err:
1070 transflag = 0;
1071 perror_reply(426, "Data connection");
1072 return;
1073
1074 file_err:
1075 transflag = 0;
1076 perror_reply(551, "Error on input file");
1077 }
1078
1079 /*
1080 * Transfer data from peer to "outstr" using the appropriate encapulation of
1081 * the data subject to Mode, Structure, and Type.
1082 *
1083 * N.B.: Form isn't handled.
1084 */
1085 static int
1086 receive_data(instr, outstr)
1087 FILE *instr, *outstr;
1088 {
1089 int c, cnt, bare_lfs;
1090 char buf[BUFSIZ];
1091 #ifdef __GNUC__
1092 (void) &bare_lfs;
1093 #endif
1094
1095 bare_lfs = 0;
1096 transflag++;
1097 if (setjmp(urgcatch)) {
1098 transflag = 0;
1099 return (-1);
1100 }
1101
1102 switch (type) {
1103
1104 case TYPE_I:
1105 case TYPE_L:
1106 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1107 if (write(fileno(outstr), buf, cnt) != cnt)
1108 goto file_err;
1109 byte_count += cnt;
1110 }
1111 if (cnt < 0)
1112 goto data_err;
1113 transflag = 0;
1114 return (0);
1115
1116 case TYPE_E:
1117 reply(553, "TYPE E not implemented.");
1118 transflag = 0;
1119 return (-1);
1120
1121 case TYPE_A:
1122 while ((c = getc(instr)) != EOF) {
1123 byte_count++;
1124 if (c == '\n')
1125 bare_lfs++;
1126 while (c == '\r') {
1127 if (ferror(outstr))
1128 goto data_err;
1129 if ((c = getc(instr)) != '\n') {
1130 (void) putc ('\r', outstr);
1131 if (c == '\0' || c == EOF)
1132 goto contin2;
1133 }
1134 }
1135 (void) putc(c, outstr);
1136 contin2: ;
1137 }
1138 fflush(outstr);
1139 if (ferror(instr))
1140 goto data_err;
1141 if (ferror(outstr))
1142 goto file_err;
1143 transflag = 0;
1144 if (bare_lfs) {
1145 lreply(226,
1146 "WARNING! %d bare linefeeds received in ASCII mode",
1147 bare_lfs);
1148 (void)printf(" File may not have transferred correctly.\r\n");
1149 }
1150 return (0);
1151 default:
1152 reply(550, "Unimplemented TYPE %d in receive_data", type);
1153 transflag = 0;
1154 return (-1);
1155 }
1156
1157 data_err:
1158 transflag = 0;
1159 perror_reply(426, "Data Connection");
1160 return (-1);
1161
1162 file_err:
1163 transflag = 0;
1164 perror_reply(452, "Error writing file");
1165 return (-1);
1166 }
1167
1168 void
1169 statfilecmd(filename)
1170 char *filename;
1171 {
1172 FILE *fin;
1173 int c;
1174 char line[LINE_MAX];
1175
1176 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1177 fin = ftpd_popen(line, "r", 0);
1178 lreply(211, "status of %s:", filename);
1179 while ((c = getc(fin)) != EOF) {
1180 if (c == '\n') {
1181 if (ferror(stdout)){
1182 perror_reply(421, "control connection");
1183 (void) ftpd_pclose(fin);
1184 dologout(1);
1185 /* NOTREACHED */
1186 }
1187 if (ferror(fin)) {
1188 perror_reply(551, filename);
1189 (void) ftpd_pclose(fin);
1190 return;
1191 }
1192 (void) putc('\r', stdout);
1193 }
1194 (void) putc(c, stdout);
1195 }
1196 (void) ftpd_pclose(fin);
1197 reply(211, "End of Status");
1198 }
1199
1200 void
1201 statcmd()
1202 {
1203 struct sockaddr_in *sin;
1204 u_char *a, *p;
1205
1206 lreply(211, "%s FTP server status:", hostname);
1207 lreply(211, "%s", version);
1208 if (isdigit(remotehost[0]))
1209 lreply(211, "Connected to %s", remotehost);
1210 else
1211 lreply(211, "Connected to %s (%s)", remotehost,
1212 inet_ntoa(his_addr.sin_addr));
1213 if (logged_in) {
1214 if (guest)
1215 lreply(211, "Logged in anonymously");
1216 else
1217 lreply(211, "Logged in as %s", pw->pw_name);
1218 } else if (askpasswd)
1219 lreply(211, "Waiting for password");
1220 else
1221 lreply(211, "Waiting for user name");
1222 printf("211- TYPE: %s", typenames[type]);
1223 if (type == TYPE_A || type == TYPE_E)
1224 printf(", FORM: %s", formnames[form]);
1225 if (type == TYPE_L)
1226 #if NBBY == 8
1227 printf(" %d", NBBY);
1228 #else
1229 printf(" %d", bytesize); /* need definition! */
1230 #endif
1231 printf("; STRUcture: %s; transfer MODE: %s\r\n",
1232 strunames[stru], modenames[mode]);
1233 if (data != -1)
1234 lreply(211, "Data connection open");
1235 else if (pdata != -1) {
1236 printf("211- in Passive mode");
1237 sin = &pasv_addr;
1238 goto printaddr;
1239 } else if (usedefault == 0) {
1240 printf("211- PORT");
1241 sin = &data_dest;
1242 printaddr:
1243 a = (u_char *) &sin->sin_addr;
1244 p = (u_char *) &sin->sin_port;
1245 #define UC(b) (((int) b) & 0xff)
1246 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1247 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1248 #undef UC
1249 } else
1250 lreply(211, "No data connection");
1251
1252 if (logged_in) {
1253 struct ftpconv *cp;
1254
1255 lreply(211, "");
1256 lreply(211, "Class: %s", curclass.classname);
1257 if (curclass.display)
1258 lreply(211, "Display file: %s", curclass.display);
1259 if (curclass.notify)
1260 lreply(211, "Notify fileglob: %s", curclass.notify);
1261 lreply(211, "Idle timeout: %d, maximum timeout: %d",
1262 curclass.timeout, curclass.maxtimeout);
1263 lreply(211, "dele, mkd, rmd, umask, chmod: %sabled",
1264 curclass.modify ? "en" : "dis");
1265 lreply(211, "Umask: %.04o", curclass.umask);
1266 for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
1267 if (cp->suffix == NULL || cp->types == NULL ||
1268 cp->command == NULL)
1269 continue;
1270 lreply(211,
1271 "Conversion: %s [%s] disable: %s, command: %s",
1272 cp->suffix, cp->types, cp->disable, cp->command);
1273 }
1274 }
1275
1276 reply(211, "End of status");
1277 }
1278
1279 void
1280 fatal(s)
1281 char *s;
1282 {
1283
1284 reply(451, "Error in server: %s\n", s);
1285 reply(221, "Closing connection due to server error.");
1286 dologout(0);
1287 /* NOTREACHED */
1288 }
1289
1290 void
1291 #if __STDC__
1292 reply(int n, const char *fmt, ...)
1293 #else
1294 reply(n, fmt, va_alist)
1295 int n;
1296 char *fmt;
1297 va_dcl
1298 #endif
1299 {
1300 va_list ap;
1301 #if __STDC__
1302 va_start(ap, fmt);
1303 #else
1304 va_start(ap);
1305 #endif
1306 (void)printf("%d ", n);
1307 (void)vprintf(fmt, ap);
1308 (void)printf("\r\n");
1309 (void)fflush(stdout);
1310 if (debug) {
1311 syslog(LOG_DEBUG, "<--- %d ", n);
1312 vsyslog(LOG_DEBUG, fmt, ap);
1313 }
1314 }
1315
1316 void
1317 #if __STDC__
1318 lreply(int n, const char *fmt, ...)
1319 #else
1320 lreply(n, fmt, va_alist)
1321 int n;
1322 char *fmt;
1323 va_dcl
1324 #endif
1325 {
1326 va_list ap;
1327 #if __STDC__
1328 va_start(ap, fmt);
1329 #else
1330 va_start(ap);
1331 #endif
1332 (void)printf("%d- ", n);
1333 (void)vprintf(fmt, ap);
1334 (void)printf("\r\n");
1335 (void)fflush(stdout);
1336 if (debug) {
1337 syslog(LOG_DEBUG, "<--- %d- ", n);
1338 vsyslog(LOG_DEBUG, fmt, ap);
1339 }
1340 }
1341
1342 static void
1343 ack(s)
1344 char *s;
1345 {
1346
1347 reply(250, "%s command successful.", s);
1348 }
1349
1350 void
1351 nack(s)
1352 char *s;
1353 {
1354
1355 reply(502, "%s command not implemented.", s);
1356 }
1357
1358 /* ARGSUSED */
1359 void
1360 yyerror(s)
1361 char *s;
1362 {
1363 char *cp;
1364
1365 if ((cp = strchr(cbuf,'\n')) != NULL)
1366 *cp = '\0';
1367 reply(500, "'%s': command not understood.", cbuf);
1368 }
1369
1370 void
1371 delete(name)
1372 char *name;
1373 {
1374 struct stat st;
1375
1376 LOGCMD("delete", name);
1377 if (stat(name, &st) < 0) {
1378 perror_reply(550, name);
1379 return;
1380 }
1381 if ((st.st_mode&S_IFMT) == S_IFDIR) {
1382 if (rmdir(name) < 0) {
1383 perror_reply(550, name);
1384 return;
1385 }
1386 goto done;
1387 }
1388 if (unlink(name) < 0) {
1389 perror_reply(550, name);
1390 return;
1391 }
1392 done:
1393 ack("DELE");
1394 }
1395
1396 void
1397 cwd(path)
1398 char *path;
1399 {
1400
1401 if (chdir(path) < 0)
1402 perror_reply(550, path);
1403 else {
1404 show_chdir_messages(250);
1405 ack("CWD");
1406 }
1407 }
1408
1409 void
1410 makedir(name)
1411 char *name;
1412 {
1413
1414 LOGCMD("mkdir", name);
1415 if (mkdir(name, 0777) < 0)
1416 perror_reply(550, name);
1417 else
1418 reply(257, "MKD command successful.");
1419 }
1420
1421 void
1422 removedir(name)
1423 char *name;
1424 {
1425
1426 LOGCMD("rmdir", name);
1427 if (rmdir(name) < 0)
1428 perror_reply(550, name);
1429 else
1430 ack("RMD");
1431 }
1432
1433 void
1434 pwd()
1435 {
1436 char path[MAXPATHLEN + 1];
1437
1438 if (getwd(path) == (char *)NULL)
1439 reply(550, "%s.", path);
1440 else
1441 reply(257, "\"%s\" is current directory.", path);
1442 }
1443
1444 char *
1445 renamefrom(name)
1446 char *name;
1447 {
1448 struct stat st;
1449
1450 if (stat(name, &st) < 0) {
1451 perror_reply(550, name);
1452 return ((char *)0);
1453 }
1454 reply(350, "File exists, ready for destination name");
1455 return (name);
1456 }
1457
1458 void
1459 renamecmd(from, to)
1460 char *from, *to;
1461 {
1462
1463 LOGCMD2("rename", from, to);
1464 if (rename(from, to) < 0)
1465 perror_reply(550, "rename");
1466 else
1467 ack("RNTO");
1468 }
1469
1470 static void
1471 dolog(sin)
1472 struct sockaddr_in *sin;
1473 {
1474 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1475 sizeof(struct in_addr), AF_INET);
1476
1477 if (hp)
1478 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1479 else
1480 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1481 sizeof(remotehost));
1482 #ifdef HASSETPROCTITLE
1483 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1484 setproctitle(proctitle);
1485 #endif /* HASSETPROCTITLE */
1486
1487 if (logging)
1488 syslog(LOG_INFO, "connection from %s", remotehost);
1489 }
1490
1491 /*
1492 * Record logout in wtmp file
1493 * and exit with supplied status.
1494 */
1495 void
1496 dologout(status)
1497 int status;
1498 {
1499 /*
1500 * Prevent reception of SIGURG from resulting in a resumption
1501 * back to the main program loop.
1502 */
1503 transflag = 0;
1504
1505 if (logged_in) {
1506 (void) seteuid((uid_t)0);
1507 logwtmp(ttyline, "", "");
1508 #if defined(KERBEROS)
1509 if (!notickets && krbtkfile_env)
1510 unlink(krbtkfile_env);
1511 #endif
1512 }
1513 /* beware of flushing buffers after a SIGPIPE */
1514 _exit(status);
1515 }
1516
1517 static void
1518 myoob(signo)
1519 int signo;
1520 {
1521 char *cp;
1522
1523 /* only process if transfer occurring */
1524 if (!transflag)
1525 return;
1526 cp = tmpline;
1527 if (getline(cp, 7, stdin) == NULL) {
1528 reply(221, "You could at least say goodbye.");
1529 dologout(0);
1530 }
1531 upper(cp);
1532 if (strcmp(cp, "ABOR\r\n") == 0) {
1533 tmpline[0] = '\0';
1534 reply(426, "Transfer aborted. Data connection closed.");
1535 reply(226, "Abort successful");
1536 longjmp(urgcatch, 1);
1537 }
1538 if (strcmp(cp, "STAT\r\n") == 0) {
1539 if (file_size != (off_t) -1)
1540 reply(213, "Status: %qd of %qd bytes transferred",
1541 byte_count, file_size);
1542 else
1543 reply(213, "Status: %qd bytes transferred", byte_count);
1544 }
1545 }
1546
1547 /*
1548 * Note: a response of 425 is not mentioned as a possible response to
1549 * the PASV command in RFC959. However, it has been blessed as
1550 * a legitimate response by Jon Postel in a telephone conversation
1551 * with Rick Adams on 25 Jan 89.
1552 */
1553 void
1554 passive()
1555 {
1556 int len;
1557 char *p, *a;
1558
1559 pdata = socket(AF_INET, SOCK_STREAM, 0);
1560 if (pdata < 0 || !logged_in) {
1561 perror_reply(425, "Can't open passive connection");
1562 return;
1563 }
1564 pasv_addr = ctrl_addr;
1565 pasv_addr.sin_port = 0;
1566 (void) seteuid((uid_t)0);
1567 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1568 (void) seteuid((uid_t)pw->pw_uid);
1569 goto pasv_error;
1570 }
1571 (void) seteuid((uid_t)pw->pw_uid);
1572 len = sizeof(pasv_addr);
1573 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1574 goto pasv_error;
1575 if (listen(pdata, 1) < 0)
1576 goto pasv_error;
1577 a = (char *) &pasv_addr.sin_addr;
1578 p = (char *) &pasv_addr.sin_port;
1579
1580 #define UC(b) (((int) b) & 0xff)
1581
1582 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1583 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1584 return;
1585
1586 pasv_error:
1587 (void) close(pdata);
1588 pdata = -1;
1589 perror_reply(425, "Can't open passive connection");
1590 return;
1591 }
1592
1593 /*
1594 * Generate unique name for file with basename "local".
1595 * The file named "local" is already known to exist.
1596 * Generates failure reply on error.
1597 */
1598 static char *
1599 gunique(local)
1600 char *local;
1601 {
1602 static char new[MAXPATHLEN];
1603 struct stat st;
1604 int count;
1605 char *cp;
1606
1607 cp = strrchr(local, '/');
1608 if (cp)
1609 *cp = '\0';
1610 if (stat(cp ? local : ".", &st) < 0) {
1611 perror_reply(553, cp ? local : ".");
1612 return ((char *) 0);
1613 }
1614 if (cp)
1615 *cp = '/';
1616 (void) strcpy(new, local);
1617 cp = new + strlen(new);
1618 *cp++ = '.';
1619 for (count = 1; count < 100; count++) {
1620 (void)sprintf(cp, "%d", count);
1621 if (stat(new, &st) < 0)
1622 return (new);
1623 }
1624 reply(452, "Unique file name cannot be created.");
1625 return (NULL);
1626 }
1627
1628 /*
1629 * Format and send reply containing system error number.
1630 */
1631 void
1632 perror_reply(code, string)
1633 int code;
1634 char *string;
1635 {
1636
1637 reply(code, "%s: %s.", string, strerror(errno));
1638 }
1639
1640 static char *onefile[] = {
1641 "",
1642 0
1643 };
1644
1645 void
1646 send_file_list(whichf)
1647 char *whichf;
1648 {
1649 struct stat st;
1650 DIR *dirp = NULL;
1651 struct dirent *dir;
1652 FILE *dout = NULL;
1653 char **dirlist, *dirname;
1654 int simple = 0;
1655 int freeglob = 0;
1656 glob_t gl;
1657 #ifdef __GNUC__
1658 (void) &dout;
1659 (void) &dirlist;
1660 (void) &simple;
1661 (void) &freeglob;
1662 #endif
1663
1664 if (strpbrk(whichf, "~{[*?") != NULL) {
1665 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1666
1667 memset(&gl, 0, sizeof(gl));
1668 freeglob = 1;
1669 if (glob(whichf, flags, 0, &gl)) {
1670 reply(550, "not found");
1671 goto out;
1672 } else if (gl.gl_pathc == 0) {
1673 errno = ENOENT;
1674 perror_reply(550, whichf);
1675 goto out;
1676 }
1677 dirlist = gl.gl_pathv;
1678 } else {
1679 onefile[0] = whichf;
1680 dirlist = onefile;
1681 simple = 1;
1682 }
1683
1684 if (setjmp(urgcatch)) {
1685 transflag = 0;
1686 goto out;
1687 }
1688 while ((dirname = *dirlist++) != NULL) {
1689 if (stat(dirname, &st) < 0) {
1690 /*
1691 * If user typed "ls -l", etc, and the client
1692 * used NLST, do what the user meant.
1693 */
1694 if (dirname[0] == '-' && *dirlist == NULL &&
1695 transflag == 0) {
1696 retrieve("/bin/ls %s", dirname);
1697 goto out;
1698 }
1699 perror_reply(550, whichf);
1700 if (dout != NULL) {
1701 (void) fclose(dout);
1702 transflag = 0;
1703 data = -1;
1704 pdata = -1;
1705 }
1706 goto out;
1707 }
1708
1709 if (S_ISREG(st.st_mode)) {
1710 if (dout == NULL) {
1711 dout = dataconn("file list", (off_t)-1, "w");
1712 if (dout == NULL)
1713 goto out;
1714 transflag++;
1715 }
1716 fprintf(dout, "%s%s\n", dirname,
1717 type == TYPE_A ? "\r" : "");
1718 byte_count += strlen(dirname) + 1;
1719 continue;
1720 } else if (!S_ISDIR(st.st_mode))
1721 continue;
1722
1723 if ((dirp = opendir(dirname)) == NULL)
1724 continue;
1725
1726 while ((dir = readdir(dirp)) != NULL) {
1727 char nbuf[MAXPATHLEN];
1728
1729 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1730 continue;
1731 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1732 dir->d_namlen == 2)
1733 continue;
1734
1735 sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1736
1737 /*
1738 * We have to do a stat to insure it's
1739 * not a directory or special file.
1740 */
1741 if (simple || (stat(nbuf, &st) == 0 &&
1742 S_ISREG(st.st_mode))) {
1743 if (dout == NULL) {
1744 dout = dataconn("file list", (off_t)-1,
1745 "w");
1746 if (dout == NULL)
1747 goto out;
1748 transflag++;
1749 }
1750 if (nbuf[0] == '.' && nbuf[1] == '/')
1751 fprintf(dout, "%s%s\n", &nbuf[2],
1752 type == TYPE_A ? "\r" : "");
1753 else
1754 fprintf(dout, "%s%s\n", nbuf,
1755 type == TYPE_A ? "\r" : "");
1756 byte_count += strlen(nbuf) + 1;
1757 }
1758 }
1759 (void) closedir(dirp);
1760 }
1761
1762 if (dout == NULL)
1763 reply(550, "No files found.");
1764 else if (ferror(dout) != 0)
1765 perror_reply(550, "Data connection");
1766 else
1767 reply(226, "Transfer complete.");
1768
1769 transflag = 0;
1770 if (dout != NULL)
1771 (void) fclose(dout);
1772 data = -1;
1773 pdata = -1;
1774 out:
1775 if (freeglob) {
1776 freeglob = 0;
1777 globfree(&gl);
1778 }
1779 }
1780