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