ftpd.c revision 1.13
1104477Ssam/*	$OpenBSD: ftpd.c,v 1.13 1996/08/08 16:22:37 downsj Exp $	*/
2104477Ssam/*	$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $	*/
3104477Ssam
4139749Simp/*
5104477Ssam * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
6104477Ssam *	The Regents of the University of California.  All rights reserved.
7104477Ssam *
8104477Ssam * Redistribution and use in source and binary forms, with or without
9104477Ssam * modification, are permitted provided that the following conditions
10104477Ssam * are met:
11104477Ssam * 1. Redistributions of source code must retain the above copyright
12104477Ssam *    notice, this list of conditions and the following disclaimer.
13104477Ssam * 2. Redistributions in binary form must reproduce the above copyright
14104477Ssam *    notice, this list of conditions and the following disclaimer in the
15104477Ssam *    documentation and/or other materials provided with the distribution.
16104477Ssam * 3. All advertising materials mentioning features or use of this software
17104477Ssam *    must display the following acknowledgement:
18104477Ssam *	This product includes software developed by the University of
19104477Ssam *	California, Berkeley and its contributors.
20104477Ssam * 4. Neither the name of the University nor the names of its contributors
21104477Ssam *    may be used to endorse or promote products derived from this software
22104477Ssam *    without specific prior written permission.
23104477Ssam *
24104477Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25104477Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26104477Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27104477Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28104477Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29104477Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30104477Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31104477Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32104477Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33104477Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34104477Ssam * SUCH DAMAGE.
35104477Ssam */
36104477Ssam
37104477Ssam#ifndef lint
38104477Ssamstatic char copyright[] =
39104477Ssam"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
40104477Ssam	The Regents of the University of California.  All rights reserved.\n";
41104477Ssam#endif /* not lint */
42104477Ssam
43104477Ssam#ifndef lint
44104477Ssam#if 0
45104477Ssamstatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
46104477Ssam#else
47104477Ssamstatic char rcsid[] = "$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $";
48104477Ssam#endif
49104477Ssam#endif /* not lint */
50104477Ssam
51104477Ssam/*
52104477Ssam * FTP server.
53104477Ssam */
54104477Ssam#include <sys/param.h>
55104477Ssam#include <sys/stat.h>
56104477Ssam#include <sys/ioctl.h>
57104477Ssam#include <sys/socket.h>
58104477Ssam#include <sys/wait.h>
59104477Ssam#include <sys/mman.h>
60104477Ssam
61104477Ssam#include <netinet/in.h>
62104477Ssam#include <netinet/in_systm.h>
63104477Ssam#include <netinet/ip.h>
64104477Ssam#include <netinet/tcp.h>
65104477Ssam
66104477Ssam#define	FTP_NAMES
67104477Ssam#include <arpa/ftp.h>
68104477Ssam#include <arpa/inet.h>
69104477Ssam#include <arpa/telnet.h>
70120915Ssam
71120915Ssam#include <ctype.h>
72104477Ssam#include <dirent.h>
73104477Ssam#include <err.h>
74104477Ssam#include <errno.h>
75104477Ssam#include <fcntl.h>
76104477Ssam#include <glob.h>
77104477Ssam#include <limits.h>
78104477Ssam#include <netdb.h>
79104477Ssam#include <pwd.h>
80104477Ssam#include <setjmp.h>
81104477Ssam#include <signal.h>
82104477Ssam#include <stdio.h>
83104477Ssam#include <stdlib.h>
84213091Sgonzo#include <string.h>
85213091Sgonzo#include <syslog.h>
86104477Ssam#include <time.h>
87104477Ssam#include <unistd.h>
88104477Ssam#include <utmp.h>
89104477Ssam
90104477Ssam#include "pathnames.h"
91104477Ssam#include "extern.h"
92104477Ssam
93104477Ssam#if __STDC__
94104477Ssam#include <stdarg.h>
95104477Ssam#else
96104477Ssam#include <varargs.h>
97104477Ssam#endif
98104477Ssam
99104477Ssamstatic char version[] = "Version 6.1/OpenBSD";
100104477Ssam
101104477Ssamextern	off_t restart_point;
102213091Sgonzoextern	char cbuf[];
103104477Ssam
104104477Ssamstruct	sockaddr_in server_addr;
105104477Ssamstruct	sockaddr_in ctrl_addr;
106136526Ssamstruct	sockaddr_in data_source;
107158705Spjdstruct	sockaddr_in data_dest;
108120915Ssamstruct	sockaddr_in his_addr;
109104477Ssamstruct	sockaddr_in pasv_addr;
110104477Ssam
111104477Ssamint	daemon_mode = 0;
112104477Ssamint	data;
113104477Ssamjmp_buf	errcatch, urgcatch;
114104477Ssamint	logged_in;
115104477Ssamstruct	passwd *pw;
116104477Ssamint	debug = 0;
117104477Ssamint	timeout = 900;    /* timeout after 15 minutes of inactivity */
118104477Ssamint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
119104477Ssamint	logging;
120104477Ssamint	high_data_ports = 0;
121104477Ssamint	guest;
122104477Ssamint	stats;
123104477Ssamint	statfd = -1;
124104477Ssamint	dochroot;
125104477Ssamint	type;
126104477Ssamint	form;
127104477Ssamint	stru;			/* avoid C keyword */
128104477Ssamint	mode;
129104477Ssamint	doutmp = 0;		/* update utmp file */
130104477Ssamint	usedefault = 1;		/* for data transfers */
131104477Ssamint	pdata = -1;		/* for passive mode */
132104477Ssamsig_atomic_t transflag;
133104477Ssamoff_t	file_size;
134104477Ssamoff_t	byte_count;
135104477Ssam#if !defined(CMASK) || CMASK == 0
136104477Ssam#undef CMASK
137104477Ssam#define CMASK 027
138104477Ssam#endif
139104477Ssamint	defumask = CMASK;		/* default umask value */
140104477Ssamchar	tmpline[7];
141104477Ssamchar	hostname[MAXHOSTNAMELEN];
142104477Ssamchar	remotehost[MAXHOSTNAMELEN];
143104477Ssamstatic char ttyline[20];
144104477Ssamchar	*tty = ttyline;		/* for klogin */
145140480Ssamstatic struct utmp utmp;	/* for utmp */
146104477Ssam
147104477Ssam#if defined(KERBEROS)
148104477Ssamint	notickets = 1;
149104477Ssamchar	*krbtkfile_env = NULL;
150104477Ssam#endif
151104477Ssam
152213091Sgonzochar	*ident = NULL;
153213091Sgonzo
154213091Sgonzo
155213091Sgonzo/*
156213091Sgonzo * Timeout intervals for retrying connections
157213091Sgonzo * to hosts that don't accept PORT cmds.  This
158213091Sgonzo * is a kludge, but given the problems with TCP...
159213091Sgonzo */
160213091Sgonzo#define	SWAITMAX	90	/* wait at most 90 seconds */
161104477Ssam#define	SWAITINT	5	/* interval between retries */
162104477Ssam
163136526Ssamint	swaitmax = SWAITMAX;
164136526Ssamint	swaitint = SWAITINT;
165104477Ssam
166104477Ssam#ifdef HASSETPROCTITLE
167104477Ssamchar	proctitle[BUFSIZ];	/* initial part of title */
168104477Ssam#endif /* HASSETPROCTITLE */
169120915Ssam
170120915Ssam#define LOGCMD(cmd, file) \
171120915Ssam	if (logging > 1) \
172104477Ssam	    syslog(LOG_INFO,"%s %s%s", cmd, \
173104477Ssam		*(file) == '/' ? "" : curdir(), file);
174104477Ssam#define LOGCMD2(cmd, file1, file2) \
175104477Ssam	 if (logging > 1) \
176112124Ssam	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
177112124Ssam		*(file1) == '/' ? "" : curdir(), file1, \
178112124Ssam		*(file2) == '/' ? "" : curdir(), file2);
179104477Ssam#define LOGBYTES(cmd, file, cnt) \
180104477Ssam	if (logging > 1) { \
181104477Ssam		if (cnt == (off_t)-1) \
182104477Ssam		    syslog(LOG_INFO,"%s %s%s", cmd, \
183104477Ssam			*(file) == '/' ? "" : curdir(), file); \
184104477Ssam		else \
185104477Ssam		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
186104477Ssam			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
187167755Ssam	}
188167755Ssam
189167755Ssamstatic void	 ack __P((char *));
190104477Ssamstatic void	 myoob __P((int));
191104477Ssamstatic int	 checkuser __P((char *, char *));
192104477Ssamstatic FILE	*dataconn __P((char *, off_t, char *));
193104477Ssamstatic void	 dolog __P((struct sockaddr_in *));
194104477Ssamstatic char	*curdir __P((void));
195104477Ssamstatic void	 end_login __P((void));
196104477Ssamstatic FILE	*getdatasock __P((char *));
197104477Ssamstatic char	*gunique __P((char *));
198104477Ssamstatic void	 lostconn __P((int));
199104477Ssamstatic void	 sigquit __P((int));
200104477Ssamstatic int	 receive_data __P((FILE *, FILE *));
201104477Ssamstatic void	 send_data __P((FILE *, FILE *, off_t, off_t, int));
202104477Ssamstatic struct passwd *
203104477Ssam		 sgetpwnam __P((char *));
204104477Ssamstatic char	*sgetsave __P((char *));
205104477Ssamstatic void	 reapchild __P((int));
206104477Ssam
207104477Ssamvoid	 logxfer __P((char *, off_t, time_t));
208104477Ssam
209104477Ssamstatic char *
210104477Ssamcurdir()
211104477Ssam{
212104477Ssam	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
213104477Ssam
214104477Ssam	if (getcwd(path, sizeof(path)-2) == NULL)
215104477Ssam		return ("");
216104477Ssam	if (path[1] != '\0')		/* special case for root dir. */
217104477Ssam		strcat(path, "/");
218104477Ssam	/* For guest account, skip / since it's chrooted */
219104477Ssam	return (guest ? path+1 : path);
220104477Ssam}
221104477Ssam
222104477Ssamint
223104477Ssammain(argc, argv, envp)
224104477Ssam	int argc;
225104477Ssam	char *argv[];
226104477Ssam	char **envp;
227104477Ssam{
228104477Ssam	int addrlen, ch, on = 1, tos;
229104477Ssam	char *cp, line[LINE_MAX];
230104477Ssam	FILE *fd;
231104477Ssam	char *argstr = "dDhlSt:T:u:Uv";
232104477Ssam
233104477Ssam	tzset();	/* in case no timezone database in ~ftp */
234104477Ssam
235104477Ssam	/* set this here so klogin can use it... */
236104477Ssam	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
237104477Ssam
238104477Ssam	while ((ch = getopt(argc, argv, argstr)) != EOF) {
239104477Ssam		switch (ch) {
240104477Ssam		case 'd':
241104477Ssam			debug = 1;
242104477Ssam			break;
243104477Ssam
244104477Ssam		case 'D':
245104477Ssam			daemon_mode = 1;
246104477Ssam			break;
247104477Ssam
248104477Ssam		case 'h':
249104477Ssam			high_data_ports = 1;
250104477Ssam			break;
251104477Ssam
252104477Ssam		case 'l':
253104477Ssam			logging++;	/* > 1 == extra logging */
254104477Ssam			break;
255104477Ssam
256104477Ssam		case 'S':
257104477Ssam			stats = 1;
258104477Ssam			break;
259104477Ssam
260104477Ssam		case 't':
261104477Ssam			timeout = atoi(optarg);
262104477Ssam			if (maxtimeout < timeout)
263104477Ssam				maxtimeout = timeout;
264104477Ssam			break;
265104477Ssam
266104477Ssam		case 'T':
267104477Ssam			maxtimeout = atoi(optarg);
268104477Ssam			if (timeout > maxtimeout)
269104477Ssam				timeout = maxtimeout;
270104477Ssam			break;
271104477Ssam
272104477Ssam		case 'u':
273120915Ssam		    {
274104477Ssam			long val = 0;
275104477Ssam
276104477Ssam			val = strtol(optarg, &optarg, 8);
277104477Ssam			if (*optarg != '\0' || val < 0)
278104477Ssam				warnx("bad value for -u");
279104477Ssam			else
280104477Ssam				defumask = val;
281104477Ssam			break;
282104477Ssam		    }
283104477Ssam
284104477Ssam		case 'U':
285104477Ssam			doutmp = 1;
286104477Ssam			break;
287104477Ssam
288104477Ssam		case 'v':
289104477Ssam			debug = 1;
290104477Ssam			break;
291104477Ssam
292104477Ssam		default:
293104477Ssam			warnx("unknown flag -%c ignored", optopt);
294104477Ssam			break;
295104477Ssam		}
296104477Ssam	}
297104477Ssam
298104477Ssam	(void) freopen(_PATH_DEVNULL, "w", stderr);
299104477Ssam
300104477Ssam	/*
301104477Ssam	 * LOG_NDELAY sets up the logging connection immediately,
302104477Ssam	 * necessary for anonymous ftp's that chroot and can't do it later.
303104477Ssam	 */
304104477Ssam	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
305104477Ssam
306104477Ssam	if (daemon_mode) {
307104477Ssam		int ctl_sock, fd;
308104477Ssam		struct servent *sv;
309104477Ssam
310104477Ssam		/*
311104477Ssam		 * Detach from parent.
312104477Ssam		 */
313104477Ssam		if (daemon(1, 1) < 0) {
314104477Ssam			syslog(LOG_ERR, "failed to become a daemon");
315104477Ssam			exit(1);
316104477Ssam		}
317104477Ssam		(void) signal(SIGCHLD, reapchild);
318104477Ssam		/*
319104477Ssam		 * Get port number for ftp/tcp.
320104477Ssam		 */
321104477Ssam		sv = getservbyname("ftp", "tcp");
322104477Ssam		if (sv == NULL) {
323104477Ssam			syslog(LOG_ERR, "getservbyname for ftp failed");
324104477Ssam			exit(1);
325104477Ssam		}
326104477Ssam		/*
327104477Ssam		 * Open a socket, bind it to the FTP port, and start
328104477Ssam		 * listening.
329104477Ssam		 */
330104477Ssam		ctl_sock = socket(AF_INET, SOCK_STREAM, 0);
331104477Ssam		if (ctl_sock < 0) {
332104477Ssam			syslog(LOG_ERR, "control socket: %m");
333104477Ssam			exit(1);
334104477Ssam		}
335104477Ssam		if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
336104477Ssam		    (char *)&on, sizeof(on)) < 0)
337104477Ssam			syslog(LOG_ERR, "control setsockopt: %m");;
338104477Ssam		server_addr.sin_family = AF_INET;
339104477Ssam		server_addr.sin_addr.s_addr = INADDR_ANY;
340104477Ssam		server_addr.sin_port = sv->s_port;
341104477Ssam		if (bind(ctl_sock, (struct sockaddr *)&server_addr,
342104477Ssam			 sizeof(server_addr))) {
343104477Ssam			syslog(LOG_ERR, "control bind: %m");
344104477Ssam			exit(1);
345104477Ssam		}
346104477Ssam		if (listen(ctl_sock, 32) < 0) {
347104477Ssam			syslog(LOG_ERR, "control listen: %m");
348104477Ssam			exit(1);
349104477Ssam		}
350104477Ssam		/*
351104477Ssam		 * Loop forever accepting connection requests and forking off
352104477Ssam		 * children to handle them.
353104477Ssam		 */
354104477Ssam		while (1) {
355104477Ssam			addrlen = sizeof(his_addr);
356104477Ssam			fd = accept(ctl_sock, (struct sockaddr *)&his_addr,
357104477Ssam				    &addrlen);
358104477Ssam			if (fork() == 0) {
359104477Ssam				/* child */
360104477Ssam				(void) dup2(fd, 0);
361104477Ssam				(void) dup2(fd, 1);
362104477Ssam				close(ctl_sock);
363104477Ssam				break;
364			}
365			close(fd);
366		}
367	} else {
368		addrlen = sizeof(his_addr);
369		if (getpeername(0, (struct sockaddr *)&his_addr,
370			        &addrlen) < 0) {
371			syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
372			exit(1);
373		}
374	}
375
376	(void) signal(SIGHUP, sigquit);
377	(void) signal(SIGINT, sigquit);
378	(void) signal(SIGQUIT, sigquit);
379	(void) signal(SIGTERM, sigquit);
380	(void) signal(SIGPIPE, lostconn);
381	(void) signal(SIGCHLD, SIG_IGN);
382	if ((long)signal(SIGURG, myoob) < 0)
383		syslog(LOG_ERR, "signal: %m");
384
385	addrlen = sizeof(ctrl_addr);
386	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
387		syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
388		exit(1);
389	}
390#ifdef IP_TOS
391	tos = IPTOS_LOWDELAY;
392	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
393		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
394#endif
395	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
396
397	/* Try to handle urgent data inline */
398#ifdef SO_OOBINLINE
399	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
400		syslog(LOG_ERR, "setsockopt: %m");
401#endif
402
403#ifdef	F_SETOWN
404	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
405		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
406#endif
407	dolog(&his_addr);
408	/*
409	 * Set up default state
410	 */
411	data = -1;
412	type = TYPE_A;
413	form = FORM_N;
414	stru = STRU_F;
415	mode = MODE_S;
416	tmpline[0] = '\0';
417
418	/* If logins are disabled, print out the message. */
419	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
420		while (fgets(line, sizeof(line), fd) != NULL) {
421			if ((cp = strchr(line, '\n')) != NULL)
422				*cp = '\0';
423			lreply(530, "%s", line);
424		}
425		(void) fflush(stdout);
426		(void) fclose(fd);
427		reply(530, "System not available.");
428		exit(0);
429	}
430	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
431		while (fgets(line, sizeof(line), fd) != NULL) {
432			if ((cp = strchr(line, '\n')) != NULL)
433				*cp = '\0';
434			lreply(220, "%s", line);
435		}
436		(void) fflush(stdout);
437		(void) fclose(fd);
438		/* reply(220,) must follow */
439	}
440	(void) gethostname(hostname, sizeof(hostname));
441	reply(220, "%s FTP server (%s) ready.", hostname, version);
442	(void) setjmp(errcatch);
443	for (;;)
444		(void) yyparse();
445	/* NOTREACHED */
446}
447
448/*
449 * Signal handlers.
450 */
451
452static void
453lostconn(signo)
454	int signo;
455{
456
457	if (debug)
458		syslog(LOG_DEBUG, "lost connection");
459	dologout(-1);
460}
461
462static void
463sigquit(signo)
464	int signo;
465{
466	syslog(LOG_ERR, "got signal %s", strsignal(signo));
467
468	dologout(-1);
469}
470
471/*
472 * Helper function for sgetpwnam().
473 */
474static char *
475sgetsave(s)
476	char *s;
477{
478	char *new = malloc((unsigned) strlen(s) + 1);
479
480	if (new == NULL) {
481		perror_reply(421, "Local resource failure: malloc");
482		dologout(1);
483		/* NOTREACHED */
484	}
485	(void) strcpy(new, s);
486	return (new);
487}
488
489/*
490 * Save the result of a getpwnam.  Used for USER command, since
491 * the data returned must not be clobbered by any other command
492 * (e.g., globbing).
493 */
494static struct passwd *
495sgetpwnam(name)
496	char *name;
497{
498	static struct passwd save;
499	struct passwd *p;
500
501	if ((p = getpwnam(name)) == NULL)
502		return (p);
503	if (save.pw_name) {
504		free(save.pw_name);
505		free(save.pw_passwd);
506		free(save.pw_gecos);
507		free(save.pw_dir);
508		free(save.pw_shell);
509	}
510	save = *p;
511	save.pw_name = sgetsave(p->pw_name);
512	save.pw_passwd = sgetsave(p->pw_passwd);
513	save.pw_gecos = sgetsave(p->pw_gecos);
514	save.pw_dir = sgetsave(p->pw_dir);
515	save.pw_shell = sgetsave(p->pw_shell);
516	return (&save);
517}
518
519static int login_attempts;	/* number of failed login attempts */
520static int askpasswd;		/* had user command, ask for passwd */
521static char curname[10];	/* current USER name */
522
523/*
524 * USER command.
525 * Sets global passwd pointer pw if named account exists and is acceptable;
526 * sets askpasswd if a PASS command is expected.  If logged in previously,
527 * need to reset state.  If name is "ftp" or "anonymous", the name is not in
528 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
529 * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
530 * requesting login privileges.  Disallow anyone who does not have a standard
531 * shell as returned by getusershell().  Disallow anyone mentioned in the file
532 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
533 */
534void
535user(name)
536	char *name;
537{
538	char *cp, *shell;
539
540	if (logged_in) {
541		if (guest) {
542			reply(530, "Can't change user from guest login.");
543			return;
544		} else if (dochroot) {
545			reply(530, "Can't change user from chroot user.");
546			return;
547		}
548		end_login();
549	}
550
551	guest = 0;
552	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
553		if (checkuser(_PATH_FTPUSERS, "ftp") ||
554		    checkuser(_PATH_FTPUSERS, "anonymous"))
555			reply(530, "User %s access denied.", name);
556		else if ((pw = sgetpwnam("ftp")) != NULL) {
557			guest = 1;
558			askpasswd = 1;
559			reply(331,
560			    "Guest login ok, type your name as password.");
561		} else
562			reply(530, "User %s unknown.", name);
563		if (!askpasswd && logging)
564			syslog(LOG_NOTICE,
565			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
566		return;
567	}
568	if (pw = sgetpwnam(name)) {
569		if ((shell = pw->pw_shell) == NULL || *shell == 0)
570			shell = _PATH_BSHELL;
571		while ((cp = getusershell()) != NULL)
572			if (strcmp(cp, shell) == 0)
573				break;
574		endusershell();
575
576		if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) {
577			reply(530, "User %s access denied.", name);
578			if (logging)
579				syslog(LOG_NOTICE,
580				    "FTP LOGIN REFUSED FROM %s, %s",
581				    remotehost, name);
582			pw = (struct passwd *) NULL;
583			return;
584		}
585	}
586	if (logging)
587		strncpy(curname, name, sizeof(curname)-1);
588#ifdef SKEY
589	if (!skey_haskey(name)) {
590		char *myskey, *skey_keyinfo __P((char *name));
591
592		myskey = skey_keyinfo(name);
593		reply(331, "Password [%s] for %s required.",
594		    myskey ? myskey : "error getting challenge", name);
595	} else
596#endif
597		reply(331, "Password required for %s.", name);
598
599	askpasswd = 1;
600	/*
601	 * Delay before reading passwd after first failed
602	 * attempt to slow down passwd-guessing programs.
603	 */
604	if (login_attempts)
605		sleep((unsigned) login_attempts);
606}
607
608/*
609 * Check if a user is in the file "fname"
610 */
611static int
612checkuser(fname, name)
613	char *fname;
614	char *name;
615{
616	FILE *fd;
617	int found = 0;
618	char *p, line[BUFSIZ];
619
620	if ((fd = fopen(fname, "r")) != NULL) {
621		while (fgets(line, sizeof(line), fd) != NULL)
622			if ((p = strchr(line, '\n')) != NULL) {
623				*p = '\0';
624				if (line[0] == '#')
625					continue;
626				if (strcmp(line, name) == 0) {
627					found = 1;
628					break;
629				}
630			}
631		(void) fclose(fd);
632	}
633	return (found);
634}
635
636/*
637 * Terminate login as previous user, if any, resetting state;
638 * used when USER command is given or login fails.
639 */
640static void
641end_login()
642{
643
644	(void) seteuid((uid_t)0);
645	if (logged_in) {
646		logwtmp(ttyline, "", "");
647		if (doutmp)
648			logout(utmp.ut_line);
649	}
650	pw = NULL;
651	logged_in = 0;
652	guest = 0;
653	dochroot = 0;
654}
655
656void
657pass(passwd)
658	char *passwd;
659{
660	int rval;
661	FILE *fd;
662	static char homedir[MAXPATHLEN];
663
664	if (logged_in || askpasswd == 0) {
665		reply(503, "Login with USER first.");
666		return;
667	}
668	askpasswd = 0;
669	if (!guest) {		/* "ftp" is only account allowed no password */
670		if (pw == NULL) {
671			rval = 1;	/* failure below */
672			goto skip;
673		}
674#if defined(KERBEROS)
675		rval = klogin(pw, "", hostname, passwd);
676		if (rval == 0)
677			goto skip;
678#endif
679#ifdef SKEY
680		if (skey_haskey(pw->pw_name) == 0 &&
681		   (skey_passcheck(pw->pw_name, passwd) != -1)) {
682			rval = 0;
683			goto skip;
684		}
685#endif
686		/* the strcmp does not catch null passwords! */
687		if (pw == NULL || *pw->pw_passwd == '\0' ||
688		    strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) {
689			rval = 1;	 /* failure */
690			goto skip;
691		}
692		rval = 0;
693
694skip:
695		/*
696		 * If rval == 1, the user failed the authentication check
697		 * above.  If rval == 0, either Kerberos or local authentication
698		 * succeeded.
699		 */
700		if (rval) {
701			reply(530, "Login incorrect.");
702			if (logging)
703				syslog(LOG_NOTICE,
704				    "FTP LOGIN FAILED FROM %s, %s",
705				    remotehost, curname);
706			pw = NULL;
707			if (login_attempts++ >= 5) {
708				syslog(LOG_NOTICE,
709				    "repeated login failures from %s",
710				    remotehost);
711				exit(0);
712			}
713			return;
714		}
715	}
716	login_attempts = 0;		/* this time successful */
717	if (setegid((gid_t)pw->pw_gid) < 0) {
718		reply(550, "Can't set gid.");
719		return;
720	}
721	(void) initgroups(pw->pw_name, pw->pw_gid);
722
723	/* open wtmp before chroot */
724	logwtmp(ttyline, pw->pw_name, remotehost);
725
726	/* open utmp before chroot */
727	if (doutmp) {
728		memset((void *)&utmp, 0, sizeof(utmp));
729		(void)time(&utmp.ut_time);
730		(void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name));
731		(void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host));
732		(void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line));
733		login(&utmp);
734	}
735
736	/* open stats file before chroot */
737	if (guest && (stats == 1) && (statfd < 0))
738		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
739			stats = 0;
740
741	logged_in = 1;
742
743	dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
744	if (guest) {
745		/*
746		 * We MUST do a chdir() after the chroot. Otherwise
747		 * the old current directory will be accessible as "."
748		 * outside the new root!
749		 */
750		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
751			reply(550, "Can't set guest privileges.");
752			goto bad;
753		}
754	} else if (dochroot) {
755		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
756			reply(550, "Can't change root.");
757			goto bad;
758		}
759	} else if (chdir(pw->pw_dir) < 0) {
760		if (chdir("/") < 0) {
761			reply(530, "User %s: can't change directory to %s.",
762			    pw->pw_name, pw->pw_dir);
763			goto bad;
764		} else
765			lreply(230, "No directory! Logging in with home=/");
766	}
767	if (seteuid((uid_t)pw->pw_uid) < 0) {
768		reply(550, "Can't set uid.");
769		goto bad;
770	}
771
772	/*
773	 * Set home directory so that use of ~ (tilde) works correctly.
774	 */
775	if (getcwd(homedir, MAXPATHLEN) != NULL)
776		setenv("HOME", homedir, 1);
777
778	/*
779	 * Display a login message, if it exists.
780	 * N.B. reply(230,) must follow the message.
781	 */
782	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
783		char *cp, line[LINE_MAX];
784
785		while (fgets(line, sizeof(line), fd) != NULL) {
786			if ((cp = strchr(line, '\n')) != NULL)
787				*cp = '\0';
788			lreply(230, "%s", line);
789		}
790		(void) fflush(stdout);
791		(void) fclose(fd);
792	}
793	if (guest) {
794		if (ident != NULL)
795			free(ident);
796		ident = strdup(passwd);
797		if (ident == (char *)NULL)
798			fatal("Ran out of memory.");
799		reply(230, "Guest login ok, access restrictions apply.");
800#ifdef HASSETPROCTITLE
801		snprintf(proctitle, sizeof(proctitle),
802		    "%s: anonymous/%.*s", remotehost,
803		    sizeof(proctitle) - sizeof(remotehost) -
804		    sizeof(": anonymous/"), passwd);
805		setproctitle(proctitle);
806#endif /* HASSETPROCTITLE */
807		if (logging)
808			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
809			    remotehost, passwd);
810	} else {
811		reply(230, "User %s logged in.", pw->pw_name);
812#ifdef HASSETPROCTITLE
813		snprintf(proctitle, sizeof(proctitle),
814		    "%s: %s", remotehost, pw->pw_name);
815		setproctitle(proctitle);
816#endif /* HASSETPROCTITLE */
817		if (logging)
818			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
819			    remotehost, pw->pw_name);
820	}
821	(void) umask(defumask);
822	return;
823bad:
824	/* Forget all about it... */
825	end_login();
826}
827
828void
829retrieve(cmd, name)
830	char *cmd, *name;
831{
832	FILE *fin, *dout;
833	struct stat st;
834	int (*closefunc) __P((FILE *));
835	time_t start;
836
837	if (cmd == 0) {
838		fin = fopen(name, "r"), closefunc = fclose;
839		st.st_size = 0;
840	} else {
841		char line[BUFSIZ];
842
843		(void) snprintf(line, sizeof(line), cmd, name);
844		name = line;
845		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
846		st.st_size = -1;
847		st.st_blksize = BUFSIZ;
848	}
849	if (fin == NULL) {
850		if (errno != 0) {
851			perror_reply(550, name);
852			if (cmd == 0) {
853				LOGCMD("get", name);
854			}
855		}
856		return;
857	}
858	byte_count = -1;
859	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
860		reply(550, "%s: not a plain file.", name);
861		goto done;
862	}
863	if (restart_point) {
864		if (type == TYPE_A) {
865			off_t i, n;
866			int c;
867
868			n = restart_point;
869			i = 0;
870			while (i++ < n) {
871				if ((c=getc(fin)) == EOF) {
872					perror_reply(550, name);
873					goto done;
874				}
875				if (c == '\n')
876					i++;
877			}
878		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
879			perror_reply(550, name);
880			goto done;
881		}
882	}
883	dout = dataconn(name, st.st_size, "w");
884	if (dout == NULL)
885		goto done;
886	time(&start);
887	send_data(fin, dout, st.st_blksize, st.st_size,
888		  (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)));
889	if ((cmd == 0) && guest && stats)
890		logxfer(name, st.st_size, start);
891	(void) fclose(dout);
892	data = -1;
893	pdata = -1;
894done:
895	if (cmd == 0)
896		LOGBYTES("get", name, byte_count);
897	(*closefunc)(fin);
898}
899
900void
901store(name, mode, unique)
902	char *name, *mode;
903	int unique;
904{
905	FILE *fout, *din;
906	struct stat st;
907	int (*closefunc) __P((FILE *));
908
909	if (unique && stat(name, &st) == 0 &&
910	    (name = gunique(name)) == NULL) {
911		LOGCMD(*mode == 'w' ? "put" : "append", name);
912		return;
913	}
914
915	if (restart_point)
916		mode = "r+";
917	fout = fopen(name, mode);
918	closefunc = fclose;
919	if (fout == NULL) {
920		perror_reply(553, name);
921		LOGCMD(*mode == 'w' ? "put" : "append", name);
922		return;
923	}
924	byte_count = -1;
925	if (restart_point) {
926		if (type == TYPE_A) {
927			off_t i, n;
928			int c;
929
930			n = restart_point;
931			i = 0;
932			while (i++ < n) {
933				if ((c=getc(fout)) == EOF) {
934					perror_reply(550, name);
935					goto done;
936				}
937				if (c == '\n')
938					i++;
939			}
940			/*
941			 * We must do this seek to "current" position
942			 * because we are changing from reading to
943			 * writing.
944			 */
945			if (fseek(fout, 0L, L_INCR) < 0) {
946				perror_reply(550, name);
947				goto done;
948			}
949		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
950			perror_reply(550, name);
951			goto done;
952		}
953	}
954	din = dataconn(name, (off_t)-1, "r");
955	if (din == NULL)
956		goto done;
957	if (receive_data(din, fout) == 0) {
958		if (unique)
959			reply(226, "Transfer complete (unique file name:%s).",
960			    name);
961		else
962			reply(226, "Transfer complete.");
963	}
964	(void) fclose(din);
965	data = -1;
966	pdata = -1;
967done:
968	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
969	(*closefunc)(fout);
970}
971
972static FILE *
973getdatasock(mode)
974	char *mode;
975{
976	int on = 1, s, t, tries;
977
978	if (data >= 0)
979		return (fdopen(data, mode));
980	(void) seteuid((uid_t)0);
981	s = socket(AF_INET, SOCK_STREAM, 0);
982	if (s < 0)
983		goto bad;
984	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
985	    (char *) &on, sizeof(on)) < 0)
986		goto bad;
987	/* anchor socket to avoid multi-homing problems */
988	data_source.sin_len = sizeof(struct sockaddr_in);
989	data_source.sin_family = AF_INET;
990	data_source.sin_addr = ctrl_addr.sin_addr;
991	for (tries = 1; ; tries++) {
992		if (bind(s, (struct sockaddr *)&data_source,
993		    sizeof(data_source)) >= 0)
994			break;
995		if (errno != EADDRINUSE || tries > 10)
996			goto bad;
997		sleep(tries);
998	}
999	(void) seteuid((uid_t)pw->pw_uid);
1000#ifdef IP_TOS
1001	on = IPTOS_THROUGHPUT;
1002	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
1003		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1004#endif
1005#ifdef TCP_NOPUSH
1006	/*
1007	 * Turn off push flag to keep sender TCP from sending short packets
1008	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
1009	 * to set the send buffer size as well, but that may not be desirable
1010	 * in heavy-load situations.
1011	 */
1012	on = 1;
1013	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
1014		syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
1015#endif
1016#ifdef SO_SNDBUF
1017	on = 65536;
1018	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
1019		syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
1020#endif
1021
1022	return (fdopen(s, mode));
1023bad:
1024	/* Return the real value of errno (close may change it) */
1025	t = errno;
1026	(void) seteuid((uid_t)pw->pw_uid);
1027	(void) close(s);
1028	errno = t;
1029	return (NULL);
1030}
1031
1032static FILE *
1033dataconn(name, size, mode)
1034	char *name;
1035	off_t size;
1036	char *mode;
1037{
1038	char sizebuf[32];
1039	FILE *file;
1040	int retry = 0, tos;
1041
1042	file_size = size;
1043	byte_count = 0;
1044	if (size != (off_t) -1) {
1045		(void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)",
1046				size);
1047	} else
1048		sizebuf[0] = '\0';
1049	if (pdata >= 0) {
1050		struct sockaddr_in from;
1051		int s, fromlen = sizeof(from);
1052
1053		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
1054		if (s < 0) {
1055			reply(425, "Can't open data connection.");
1056			(void) close(pdata);
1057			pdata = -1;
1058			return (NULL);
1059		}
1060		if (ntohs(from.sin_port) < IPPORT_RESERVED) {
1061			perror_reply(425, "Can't build data connection");
1062			(void) close(pdata);
1063			(void) close(s);
1064			pdata = -1;
1065			return (NULL);
1066		}
1067		if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) {
1068			perror_reply(435, "Can't build data connection");
1069			(void) close(pdata);
1070			(void) close(s);
1071			pdata = -1;
1072			return (NULL);
1073		}
1074		(void) close(pdata);
1075		pdata = s;
1076#ifdef IP_TOS
1077		tos = IPTOS_THROUGHPUT;
1078		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
1079		    sizeof(int));
1080#endif
1081		reply(150, "Opening %s mode data connection for '%s'%s.",
1082		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1083		return (fdopen(pdata, mode));
1084	}
1085	if (data >= 0) {
1086		reply(125, "Using existing data connection for '%s'%s.",
1087		    name, sizebuf);
1088		usedefault = 1;
1089		return (fdopen(data, mode));
1090	}
1091	if (usedefault)
1092		data_dest = his_addr;
1093	usedefault = 1;
1094	file = getdatasock(mode);
1095	if (file == NULL) {
1096		reply(425, "Can't create data socket (%s,%d): %s.",
1097		    inet_ntoa(data_source.sin_addr),
1098		    ntohs(data_source.sin_port), strerror(errno));
1099		return (NULL);
1100	}
1101	data = fileno(file);
1102
1103	/*
1104	 * attempt to connect to reserved port on client machine;
1105	 * this looks like an attack
1106	 */
1107	if (ntohs(data_dest.sin_port) < IPPORT_RESERVED) {
1108		perror_reply(425, "Can't build data connection");
1109		(void) fclose(file);
1110		data = -1;
1111		return NULL;
1112	}
1113	if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) {
1114		perror_reply(435, "Can't build data connection");
1115		(void) fclose(file);
1116		data = -1;
1117		return NULL;
1118	}
1119	while (connect(data, (struct sockaddr *)&data_dest,
1120	    sizeof(data_dest)) < 0) {
1121		if (errno == EADDRINUSE && retry < swaitmax) {
1122			sleep((unsigned) swaitint);
1123			retry += swaitint;
1124			continue;
1125		}
1126		perror_reply(425, "Can't build data connection");
1127		(void) fclose(file);
1128		data = -1;
1129		return (NULL);
1130	}
1131	reply(150, "Opening %s mode data connection for '%s'%s.",
1132	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1133	return (file);
1134}
1135
1136/*
1137 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1138 * encapsulation of the data subject to Mode, Structure, and Type.
1139 *
1140 * NB: Form isn't handled.
1141 */
1142static void
1143send_data(instr, outstr, blksize, filesize, isreg)
1144	FILE *instr, *outstr;
1145	off_t blksize;
1146	off_t filesize;
1147	int isreg;
1148{
1149	int c, cnt, filefd, netfd;
1150	char *buf, *bp;
1151	size_t len;
1152
1153	transflag++;
1154	if (setjmp(urgcatch)) {
1155		transflag = 0;
1156		return;
1157	}
1158	switch (type) {
1159
1160	case TYPE_A:
1161		while ((c = getc(instr)) != EOF) {
1162			byte_count++;
1163			if (c == '\n') {
1164				if (ferror(outstr))
1165					goto data_err;
1166				(void) putc('\r', outstr);
1167			}
1168			(void) putc(c, outstr);
1169		}
1170		fflush(outstr);
1171		transflag = 0;
1172		if (ferror(instr))
1173			goto file_err;
1174		if (ferror(outstr))
1175			goto data_err;
1176		reply(226, "Transfer complete.");
1177		return;
1178
1179	case TYPE_I:
1180	case TYPE_L:
1181		/*
1182		 * isreg is only set if we are not doing restart and we
1183		 * are sending a regular file
1184		 */
1185		netfd = fileno(outstr);
1186		filefd = fileno(instr);
1187
1188		if (isreg && filesize < (off_t)16 * 1024 * 1024) {
1189			buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
1190				   (off_t)0);
1191			if (!buf) {
1192				syslog(LOG_WARNING, "mmap(%lu): %m",
1193				       (unsigned long)filesize);
1194				goto oldway;
1195			}
1196			bp = buf;
1197			len = filesize;
1198			do {
1199				cnt = write(netfd, bp, len);
1200				len -= cnt;
1201				bp += cnt;
1202				if (cnt > 0) byte_count += cnt;
1203			} while(cnt > 0 && len > 0);
1204
1205			transflag = 0;
1206			munmap(buf, (size_t)filesize);
1207			if (cnt < 0)
1208				goto data_err;
1209			reply(226, "Transfer complete.");
1210			return;
1211		}
1212
1213oldway:
1214		if ((buf = malloc((u_int)blksize)) == NULL) {
1215			transflag = 0;
1216			perror_reply(451, "Local resource failure: malloc");
1217			return;
1218		}
1219
1220		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1221		    write(netfd, buf, cnt) == cnt)
1222			byte_count += cnt;
1223		transflag = 0;
1224		(void)free(buf);
1225		if (cnt != 0) {
1226			if (cnt < 0)
1227				goto file_err;
1228			goto data_err;
1229		}
1230		reply(226, "Transfer complete.");
1231		return;
1232	default:
1233		transflag = 0;
1234		reply(550, "Unimplemented TYPE %d in send_data", type);
1235		return;
1236	}
1237
1238data_err:
1239	transflag = 0;
1240	perror_reply(426, "Data connection");
1241	return;
1242
1243file_err:
1244	transflag = 0;
1245	perror_reply(551, "Error on input file");
1246}
1247
1248/*
1249 * Transfer data from peer to "outstr" using the appropriate encapulation of
1250 * the data subject to Mode, Structure, and Type.
1251 *
1252 * N.B.: Form isn't handled.
1253 */
1254static int
1255receive_data(instr, outstr)
1256	FILE *instr, *outstr;
1257{
1258	int c;
1259	int cnt, bare_lfs = 0;
1260	char buf[BUFSIZ];
1261
1262	transflag++;
1263	if (setjmp(urgcatch)) {
1264		transflag = 0;
1265		return (-1);
1266	}
1267	switch (type) {
1268
1269	case TYPE_I:
1270	case TYPE_L:
1271		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1272			if (write(fileno(outstr), buf, cnt) != cnt)
1273				goto file_err;
1274			byte_count += cnt;
1275		}
1276		if (cnt < 0)
1277			goto data_err;
1278		transflag = 0;
1279		return (0);
1280
1281	case TYPE_E:
1282		reply(553, "TYPE E not implemented.");
1283		transflag = 0;
1284		return (-1);
1285
1286	case TYPE_A:
1287		while ((c = getc(instr)) != EOF) {
1288			byte_count++;
1289			if (c == '\n')
1290				bare_lfs++;
1291			while (c == '\r') {
1292				if (ferror(outstr))
1293					goto data_err;
1294				if ((c = getc(instr)) != '\n') {
1295					(void) putc ('\r', outstr);
1296					if (c == '\0' || c == EOF)
1297						goto contin2;
1298				}
1299			}
1300			(void) putc(c, outstr);
1301	contin2:	;
1302		}
1303		fflush(outstr);
1304		if (ferror(instr))
1305			goto data_err;
1306		if (ferror(outstr))
1307			goto file_err;
1308		transflag = 0;
1309		if (bare_lfs) {
1310			lreply(226,
1311		"WARNING! %d bare linefeeds received in ASCII mode",
1312			    bare_lfs);
1313		(void)printf("   File may not have transferred correctly.\r\n");
1314		}
1315		return (0);
1316	default:
1317		reply(550, "Unimplemented TYPE %d in receive_data", type);
1318		transflag = 0;
1319		return (-1);
1320	}
1321
1322data_err:
1323	transflag = 0;
1324	perror_reply(426, "Data Connection");
1325	return (-1);
1326
1327file_err:
1328	transflag = 0;
1329	perror_reply(452, "Error writing file");
1330	return (-1);
1331}
1332
1333void
1334statfilecmd(filename)
1335	char *filename;
1336{
1337	FILE *fin;
1338	int c;
1339	char line[LINE_MAX];
1340
1341	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1342	fin = ftpd_popen(line, "r");
1343	lreply(211, "status of %s:", filename);
1344	while ((c = getc(fin)) != EOF) {
1345		if (c == '\n') {
1346			if (ferror(stdout)){
1347				perror_reply(421, "control connection");
1348				(void) ftpd_pclose(fin);
1349				dologout(1);
1350				/* NOTREACHED */
1351			}
1352			if (ferror(fin)) {
1353				perror_reply(551, filename);
1354				(void) ftpd_pclose(fin);
1355				return;
1356			}
1357			(void) putc('\r', stdout);
1358		}
1359		(void) putc(c, stdout);
1360	}
1361	(void) ftpd_pclose(fin);
1362	reply(211, "End of Status");
1363}
1364
1365void
1366statcmd()
1367{
1368	struct sockaddr_in *sin;
1369	u_char *a, *p;
1370
1371	lreply(211, "%s FTP server status:", hostname, version);
1372	printf("     %s\r\n", version);
1373	printf("     Connected to %s", remotehost);
1374	if (!isdigit(remotehost[0]))
1375		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1376	printf("\r\n");
1377	if (logged_in) {
1378		if (guest)
1379			printf("     Logged in anonymously\r\n");
1380		else
1381			printf("     Logged in as %s\r\n", pw->pw_name);
1382	} else if (askpasswd)
1383		printf("     Waiting for password\r\n");
1384	else
1385		printf("     Waiting for user name\r\n");
1386	printf("     TYPE: %s", typenames[type]);
1387	if (type == TYPE_A || type == TYPE_E)
1388		printf(", FORM: %s", formnames[form]);
1389	if (type == TYPE_L)
1390#if NBBY == 8
1391		printf(" %d", NBBY);
1392#else
1393		printf(" %d", bytesize);	/* need definition! */
1394#endif
1395	printf("; STRUcture: %s; transfer MODE: %s\r\n",
1396	    strunames[stru], modenames[mode]);
1397	if (data != -1)
1398		printf("     Data connection open\r\n");
1399	else if (pdata != -1) {
1400		printf("     in Passive mode");
1401		sin = &pasv_addr;
1402		goto printaddr;
1403	} else if (usedefault == 0) {
1404		printf("     PORT");
1405		sin = &data_dest;
1406printaddr:
1407		a = (u_char *) &sin->sin_addr;
1408		p = (u_char *) &sin->sin_port;
1409#define UC(b) (((int) b) & 0xff)
1410		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1411			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1412#undef UC
1413	} else
1414		printf("     No data connection\r\n");
1415	reply(211, "End of status");
1416}
1417
1418void
1419fatal(s)
1420	char *s;
1421{
1422
1423	reply(451, "Error in server: %s\n", s);
1424	reply(221, "Closing connection due to server error.");
1425	dologout(0);
1426	/* NOTREACHED */
1427}
1428
1429void
1430#if __STDC__
1431reply(int n, const char *fmt, ...)
1432#else
1433reply(n, fmt, va_alist)
1434	int n;
1435	char *fmt;
1436        va_dcl
1437#endif
1438{
1439	va_list ap;
1440#if __STDC__
1441	va_start(ap, fmt);
1442#else
1443	va_start(ap);
1444#endif
1445	(void)printf("%d ", n);
1446	(void)vprintf(fmt, ap);
1447	(void)printf("\r\n");
1448	(void)fflush(stdout);
1449	if (debug) {
1450		syslog(LOG_DEBUG, "<--- %d ", n);
1451		vsyslog(LOG_DEBUG, fmt, ap);
1452	}
1453}
1454
1455void
1456#if __STDC__
1457lreply(int n, const char *fmt, ...)
1458#else
1459lreply(n, fmt, va_alist)
1460	int n;
1461	char *fmt;
1462        va_dcl
1463#endif
1464{
1465	va_list ap;
1466#if __STDC__
1467	va_start(ap, fmt);
1468#else
1469	va_start(ap);
1470#endif
1471	(void)printf("%d- ", n);
1472	(void)vprintf(fmt, ap);
1473	(void)printf("\r\n");
1474	(void)fflush(stdout);
1475	if (debug) {
1476		syslog(LOG_DEBUG, "<--- %d- ", n);
1477		vsyslog(LOG_DEBUG, fmt, ap);
1478	}
1479}
1480
1481static void
1482ack(s)
1483	char *s;
1484{
1485
1486	reply(250, "%s command successful.", s);
1487}
1488
1489void
1490nack(s)
1491	char *s;
1492{
1493
1494	reply(502, "%s command not implemented.", s);
1495}
1496
1497/* ARGSUSED */
1498void
1499yyerror(s)
1500	char *s;
1501{
1502	char *cp;
1503
1504	if (cp = strchr(cbuf,'\n'))
1505		*cp = '\0';
1506	reply(500, "'%s': command not understood.", cbuf);
1507}
1508
1509void
1510delete(name)
1511	char *name;
1512{
1513	struct stat st;
1514
1515	LOGCMD("delete", name);
1516	if (stat(name, &st) < 0) {
1517		perror_reply(550, name);
1518		return;
1519	}
1520	if ((st.st_mode&S_IFMT) == S_IFDIR) {
1521		if (rmdir(name) < 0) {
1522			perror_reply(550, name);
1523			return;
1524		}
1525		goto done;
1526	}
1527	if (unlink(name) < 0) {
1528		perror_reply(550, name);
1529		return;
1530	}
1531done:
1532	ack("DELE");
1533}
1534
1535void
1536cwd(path)
1537	char *path;
1538{
1539
1540	if (chdir(path) < 0)
1541		perror_reply(550, path);
1542	else
1543		ack("CWD");
1544}
1545
1546void
1547makedir(name)
1548	char *name;
1549{
1550
1551	LOGCMD("mkdir", name);
1552	if (mkdir(name, 0777) < 0)
1553		perror_reply(550, name);
1554	else
1555		reply(257, "MKD command successful.");
1556}
1557
1558void
1559removedir(name)
1560	char *name;
1561{
1562
1563	LOGCMD("rmdir", name);
1564	if (rmdir(name) < 0)
1565		perror_reply(550, name);
1566	else
1567		ack("RMD");
1568}
1569
1570void
1571pwd()
1572{
1573	char path[MAXPATHLEN + 1];
1574
1575	if (getwd(path) == (char *)NULL)
1576		reply(550, "%s.", path);
1577	else
1578		reply(257, "\"%s\" is current directory.", path);
1579}
1580
1581char *
1582renamefrom(name)
1583	char *name;
1584{
1585	struct stat st;
1586
1587	if (stat(name, &st) < 0) {
1588		perror_reply(550, name);
1589		return ((char *)0);
1590	}
1591	reply(350, "File exists, ready for destination name");
1592	return (name);
1593}
1594
1595void
1596renamecmd(from, to)
1597	char *from, *to;
1598{
1599
1600	LOGCMD2("rename", from, to);
1601	if (rename(from, to) < 0)
1602		perror_reply(550, "rename");
1603	else
1604		ack("RNTO");
1605}
1606
1607static void
1608dolog(sin)
1609	struct sockaddr_in *sin;
1610{
1611	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1612		sizeof(struct in_addr), AF_INET);
1613
1614	if (hp)
1615		(void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1616	else
1617		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1618		    sizeof(remotehost));
1619#ifdef HASSETPROCTITLE
1620	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1621	setproctitle(proctitle);
1622#endif /* HASSETPROCTITLE */
1623
1624	if (logging)
1625		syslog(LOG_INFO, "connection from %s", remotehost);
1626}
1627
1628/*
1629 * Record logout in wtmp file
1630 * and exit with supplied status.
1631 */
1632void
1633dologout(status)
1634	int status;
1635{
1636
1637	if (logged_in) {
1638		(void) seteuid((uid_t)0);
1639		logwtmp(ttyline, "", "");
1640		if (doutmp)
1641			logout(utmp.ut_line);
1642#if defined(KERBEROS)
1643		if (!notickets && krbtkfile_env)
1644			unlink(krbtkfile_env);
1645#endif
1646	}
1647	/* beware of flushing buffers after a SIGPIPE */
1648	_exit(status);
1649}
1650
1651static void
1652myoob(signo)
1653	int signo;
1654{
1655	char *cp;
1656
1657	/* only process if transfer occurring */
1658	if (!transflag)
1659		return;
1660	cp = tmpline;
1661	if (getline(cp, 7, stdin) == NULL) {
1662		reply(221, "You could at least say goodbye.");
1663		dologout(0);
1664	}
1665	upper(cp);
1666	if (strcmp(cp, "ABOR\r\n") == 0) {
1667		tmpline[0] = '\0';
1668		reply(426, "Transfer aborted. Data connection closed.");
1669		reply(226, "Abort successful");
1670		longjmp(urgcatch, 1);
1671	}
1672	if (strcmp(cp, "STAT\r\n") == 0) {
1673		if (file_size != (off_t) -1)
1674			reply(213, "Status: %qd of %qd bytes transferred",
1675			    byte_count, file_size);
1676		else
1677			reply(213, "Status: %qd bytes transferred", byte_count);
1678	}
1679}
1680
1681/*
1682 * Note: a response of 425 is not mentioned as a possible response to
1683 *	the PASV command in RFC959. However, it has been blessed as
1684 *	a legitimate response by Jon Postel in a telephone conversation
1685 *	with Rick Adams on 25 Jan 89.
1686 */
1687void
1688passive()
1689{
1690	int len, on;
1691	u_short port;
1692	char *p, *a;
1693
1694	if (pw == NULL) {
1695		reply(530, "Please login with USER and PASS");
1696		return;
1697	}
1698	pdata = socket(AF_INET, SOCK_STREAM, 0);
1699	if (pdata < 0) {
1700		perror_reply(425, "Can't open passive connection");
1701		return;
1702	}
1703
1704	on = high_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
1705	if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
1706		       (char *)&on, sizeof(on)) < 0)
1707		goto pasv_error;
1708
1709	pasv_addr = ctrl_addr;
1710	pasv_addr.sin_port = 0;
1711	if (bind(pdata, (struct sockaddr *)&pasv_addr,
1712		 sizeof(pasv_addr)) < 0)
1713		goto pasv_error;
1714
1715	len = sizeof(pasv_addr);
1716	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1717		goto pasv_error;
1718	if (listen(pdata, 1) < 0)
1719		goto pasv_error;
1720	a = (char *) &pasv_addr.sin_addr;
1721	p = (char *) &pasv_addr.sin_port;
1722
1723#define UC(b) (((int) b) & 0xff)
1724
1725	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1726		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1727	return;
1728
1729pasv_error:
1730	(void) close(pdata);
1731	pdata = -1;
1732	perror_reply(425, "Can't open passive connection");
1733	return;
1734}
1735
1736/*
1737 * Generate unique name for file with basename "local".
1738 * The file named "local" is already known to exist.
1739 * Generates failure reply on error.
1740 */
1741static char *
1742gunique(local)
1743	char *local;
1744{
1745	static char new[MAXPATHLEN];
1746	struct stat st;
1747	int count;
1748	char *cp;
1749
1750	cp = strrchr(local, '/');
1751	if (cp)
1752		*cp = '\0';
1753	if (stat(cp ? local : ".", &st) < 0) {
1754		perror_reply(553, cp ? local : ".");
1755		return ((char *) 0);
1756	}
1757	if (cp)
1758		*cp = '/';
1759	(void) strncpy(new, local, sizeof(new));
1760	cp = new + strlen(new);
1761	*cp++ = '.';
1762	for (count = 1; count < 100; count++) {
1763		(void)snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1764		if (stat(new, &st) < 0)
1765			return (new);
1766	}
1767	reply(452, "Unique file name cannot be created.");
1768	return (NULL);
1769}
1770
1771/*
1772 * Format and send reply containing system error number.
1773 */
1774void
1775perror_reply(code, string)
1776	int code;
1777	char *string;
1778{
1779
1780	reply(code, "%s: %s.", string, strerror(errno));
1781}
1782
1783static char *onefile[] = {
1784	"",
1785	0
1786};
1787
1788void
1789send_file_list(whichf)
1790	char *whichf;
1791{
1792	struct stat st;
1793	DIR *dirp = NULL;
1794	struct dirent *dir;
1795	FILE *dout = NULL;
1796	char **dirlist, *dirname;
1797	int simple = 0;
1798	int freeglob = 0;
1799	glob_t gl;
1800
1801	if (strpbrk(whichf, "~{[*?") != NULL) {
1802		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1803
1804		memset(&gl, 0, sizeof(gl));
1805		freeglob = 1;
1806		if (glob(whichf, flags, 0, &gl)) {
1807			reply(550, "not found");
1808			goto out;
1809		} else if (gl.gl_pathc == 0) {
1810			errno = ENOENT;
1811			perror_reply(550, whichf);
1812			goto out;
1813		}
1814		dirlist = gl.gl_pathv;
1815	} else {
1816		onefile[0] = whichf;
1817		dirlist = onefile;
1818		simple = 1;
1819	}
1820
1821	if (setjmp(urgcatch)) {
1822		transflag = 0;
1823		goto out;
1824	}
1825	while (dirname = *dirlist++) {
1826		if (stat(dirname, &st) < 0) {
1827			/*
1828			 * If user typed "ls -l", etc, and the client
1829			 * used NLST, do what the user meant.
1830			 */
1831			if (dirname[0] == '-' && *dirlist == NULL &&
1832			    transflag == 0) {
1833				retrieve("/bin/ls %s", dirname);
1834				goto out;
1835			}
1836			perror_reply(550, whichf);
1837			if (dout != NULL) {
1838				(void) fclose(dout);
1839				transflag = 0;
1840				data = -1;
1841				pdata = -1;
1842			}
1843			goto out;
1844		}
1845
1846		if (S_ISREG(st.st_mode)) {
1847			if (dout == NULL) {
1848				dout = dataconn("file list", (off_t)-1, "w");
1849				if (dout == NULL)
1850					goto out;
1851				transflag++;
1852			}
1853			fprintf(dout, "%s%s\n", dirname,
1854				type == TYPE_A ? "\r" : "");
1855			byte_count += strlen(dirname) + 1;
1856			continue;
1857		} else if (!S_ISDIR(st.st_mode))
1858			continue;
1859
1860		if ((dirp = opendir(dirname)) == NULL)
1861			continue;
1862
1863		while ((dir = readdir(dirp)) != NULL) {
1864			char nbuf[MAXPATHLEN];
1865
1866			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1867				continue;
1868			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1869			    dir->d_namlen == 2)
1870				continue;
1871
1872			snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname,
1873				 dir->d_name);
1874
1875			/*
1876			 * We have to do a stat to insure it's
1877			 * not a directory or special file.
1878			 */
1879			if (simple || (stat(nbuf, &st) == 0 &&
1880			    S_ISREG(st.st_mode))) {
1881				if (dout == NULL) {
1882					dout = dataconn("file list", (off_t)-1,
1883						"w");
1884					if (dout == NULL)
1885						goto out;
1886					transflag++;
1887				}
1888				if (nbuf[0] == '.' && nbuf[1] == '/')
1889					fprintf(dout, "%s%s\n", &nbuf[2],
1890						type == TYPE_A ? "\r" : "");
1891				else
1892					fprintf(dout, "%s%s\n", nbuf,
1893						type == TYPE_A ? "\r" : "");
1894				byte_count += strlen(nbuf) + 1;
1895			}
1896		}
1897		(void) closedir(dirp);
1898	}
1899
1900	if (dout == NULL)
1901		reply(550, "No files found.");
1902	else if (ferror(dout) != 0)
1903		perror_reply(550, "Data connection");
1904	else
1905		reply(226, "Transfer complete.");
1906
1907	transflag = 0;
1908	if (dout != NULL)
1909		(void) fclose(dout);
1910	data = -1;
1911	pdata = -1;
1912out:
1913	if (freeglob) {
1914		freeglob = 0;
1915		globfree(&gl);
1916	}
1917}
1918
1919static void
1920reapchild(signo)
1921	int signo;
1922{
1923	while (wait3(NULL, WNOHANG, NULL) > 0);
1924}
1925
1926void
1927logxfer(name, size, start)
1928	char *name;
1929	off_t size;
1930	time_t start;
1931{
1932	char buf[1024];
1933	char path[MAXPATHLEN + 1];
1934	time_t now;
1935
1936	if ((statfd >= 0) && (getwd(path) != NULL)) {
1937		time(&now);
1938		snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%qd!%ld\n",
1939			 ctime(&now)+4, ident, remotehost,
1940			 path, name, size, now - start + (now == start));
1941		write(statfd, buf, strlen(buf));
1942	}
1943}
1944