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