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