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