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