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