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