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