11592Srgrimes/*
21592Srgrimes * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
31592Srgrimes *	The Regents of the University of California.  All rights reserved.
41592Srgrimes *
51592Srgrimes * Redistribution and use in source and binary forms, with or without
61592Srgrimes * modification, are permitted provided that the following conditions
71592Srgrimes * are met:
81592Srgrimes * 1. Redistributions of source code must retain the above copyright
91592Srgrimes *    notice, this list of conditions and the following disclaimer.
101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111592Srgrimes *    notice, this list of conditions and the following disclaimer in the
121592Srgrimes *    documentation and/or other materials provided with the distribution.
13262136Sbrueffer * 3. Neither the name of the University nor the names of its contributors
141592Srgrimes *    may be used to endorse or promote products derived from this software
151592Srgrimes *    without specific prior written permission.
161592Srgrimes *
171592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271592Srgrimes * SUCH DAMAGE.
281592Srgrimes */
291592Srgrimes
3017478Smarkm#if 0
311592Srgrimes#ifndef lint
321592Srgrimesstatic char copyright[] =
331592Srgrimes"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
341592Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351592Srgrimes#endif /* not lint */
3617478Smarkm#endif
371592Srgrimes
3831329Scharnier#ifndef lint
3917478Smarkm#if 0
401592Srgrimesstatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
4131329Scharnier#endif
421592Srgrimes#endif /* not lint */
431592Srgrimes
44137859Syar#include <sys/cdefs.h>
45137859Syar__FBSDID("$FreeBSD: releng/11.0/libexec/ftpd/ftpd.c 301517 2016-06-06 20:00:13Z lidl $");
46137859Syar
471592Srgrimes/*
481592Srgrimes * FTP server.
491592Srgrimes */
501592Srgrimes#include <sys/param.h>
511592Srgrimes#include <sys/ioctl.h>
5266907Swollman#include <sys/mman.h>
531592Srgrimes#include <sys/socket.h>
5466907Swollman#include <sys/stat.h>
5566907Swollman#include <sys/time.h>
561592Srgrimes#include <sys/wait.h>
571592Srgrimes
581592Srgrimes#include <netinet/in.h>
591592Srgrimes#include <netinet/in_systm.h>
601592Srgrimes#include <netinet/ip.h>
618240Swollman#include <netinet/tcp.h>
621592Srgrimes
631592Srgrimes#define	FTP_NAMES
641592Srgrimes#include <arpa/ftp.h>
651592Srgrimes#include <arpa/inet.h>
661592Srgrimes#include <arpa/telnet.h>
671592Srgrimes
681592Srgrimes#include <ctype.h>
691592Srgrimes#include <dirent.h>
701592Srgrimes#include <err.h>
711592Srgrimes#include <errno.h>
721592Srgrimes#include <fcntl.h>
731592Srgrimes#include <glob.h>
741592Srgrimes#include <limits.h>
751592Srgrimes#include <netdb.h>
761592Srgrimes#include <pwd.h>
7725187Sdavidn#include <grp.h>
7888763Sache#include <opie.h>
791592Srgrimes#include <signal.h>
80132929Syar#include <stdint.h>
811592Srgrimes#include <stdio.h>
821592Srgrimes#include <stdlib.h>
831592Srgrimes#include <string.h>
841592Srgrimes#include <syslog.h>
851592Srgrimes#include <time.h>
861592Srgrimes#include <unistd.h>
8713139Speter#include <libutil.h>
8825101Sdavidn#ifdef	LOGIN_CAP
8925101Sdavidn#include <login_cap.h>
9025101Sdavidn#endif
911592Srgrimes
9274874Smarkm#ifdef USE_PAM
9351433Smarkm#include <security/pam_appl.h>
9451433Smarkm#endif
9551433Smarkm
96301241Slidl#ifdef USE_BLACKLIST
97301241Slidl#include "blacklist_client.h"
98301241Slidl#endif
99301241Slidl
1001592Srgrimes#include "pathnames.h"
1011592Srgrimes#include "extern.h"
1021592Srgrimes
1031592Srgrimes#include <stdarg.h>
1041592Srgrimes
10525165Sdavidnstatic char version[] = "Version 6.00LS";
10625165Sdavidn#undef main
1071592Srgrimes
10856668Sshinunion sockunion ctrl_addr;
10956668Sshinunion sockunion data_source;
11056668Sshinunion sockunion data_dest;
11156668Sshinunion sockunion his_addr;
11256668Sshinunion sockunion pasv_addr;
1131592Srgrimes
11415196Sdgint	daemon_mode;
1151592Srgrimesint	data;
116109742Syarint	dataport;
117110037Syarint	hostinfo = 1;	/* print host-specific info in messages */
1181592Srgrimesint	logged_in;
1191592Srgrimesstruct	passwd *pw;
120110036Syarchar	*homedir;
12176096Smarkmint	ftpdebug;
1221592Srgrimesint	timeout = 900;    /* timeout after 15 minutes of inactivity */
1231592Srgrimesint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
1241592Srgrimesint	logging;
1259933Spstint	restricted_data_ports = 1;
12617435Spstint	paranoid = 1;	  /* be extra careful about security */
12720042Storstenbint	anon_only = 0;    /* Only anonymous ftp allowed */
128168849Syarint	assumeutf8 = 0;   /* Assume that server file names are in UTF-8 */
1291592Srgrimesint	guest;
13017435Spstint	dochroot;
131137862Syarchar	*chrootdir;
132102311Syarint	dowtmp = 1;
1336740Sguidoint	stats;
1346740Sguidoint	statfd = -1;
1351592Srgrimesint	type;
1361592Srgrimesint	form;
1371592Srgrimesint	stru;			/* avoid C keyword */
1381592Srgrimesint	mode;
1391592Srgrimesint	usedefault = 1;		/* for data transfers */
1401592Srgrimesint	pdata = -1;		/* for passive mode */
141137861Syarint	readonly = 0;		/* Server is in readonly mode.	*/
142137861Syarint	noepsv = 0;		/* EPSV command is disabled.	*/
143137861Syarint	noretr = 0;		/* RETR command is disabled.	*/
144137861Syarint	noguestretr = 0;	/* RETR command is disabled for anon users. */
145137861Syarint	noguestmkd = 0;		/* MKD command is disabled for anon users. */
146137861Syarint	noguestmod = 1;		/* anon users may not modify existing files. */
14782460Snik
1481592Srgrimesoff_t	file_size;
1491592Srgrimesoff_t	byte_count;
1501592Srgrimes#if !defined(CMASK) || CMASK == 0
1511592Srgrimes#undef CMASK
1521592Srgrimes#define CMASK 027
1531592Srgrimes#endif
1541592Srgrimesint	defumask = CMASK;		/* default umask value */
1551592Srgrimeschar	tmpline[7];
15627650Sdavidnchar	*hostname;
15778153Sddint	epsvall = 0;
15878153Sdd
15925283Sdavidn#ifdef VIRTUAL_HOSTING
16025283Sdavidnchar	*ftpuser;
16125283Sdavidn
16225283Sdavidnstatic struct ftphost {
16325283Sdavidn	struct ftphost	*next;
16457124Sshin	struct addrinfo *hostinfo;
16525283Sdavidn	char		*hostname;
16625283Sdavidn	char		*anonuser;
16725283Sdavidn	char		*statfile;
16825283Sdavidn	char		*welcome;
16925283Sdavidn	char		*loginmsg;
17025283Sdavidn} *thishost, *firsthost;
17125283Sdavidn
17225283Sdavidn#endif
173137983Syarchar	remotehost[NI_MAXHOST];
1746740Sguidochar	*ident = NULL;
17517435Spst
176202209Sedstatic char	wtmpid[20];
17717435Spst
17874874Smarkm#ifdef USE_PAM
17990148Simpstatic int	auth_pam(struct passwd**, const char*);
180137861Syarpam_handle_t	*pamh = NULL;
18188763Sache#endif
18279469Smarkm
183137861Syarstatic struct opie	opiedata;
184137861Syarstatic char		opieprompt[OPIE_CHALLENGE_MAX+1];
185137861Syarstatic int		pwok;
18617478Smarkm
187154630Syarchar	*pid_file = NULL; /* means default location to pidfile(3) */
18817483Sjulian
1891592Srgrimes/*
19074470Sjlemon * Limit number of pathnames that glob can return.
19174470Sjlemon * A limit of 0 indicates the number of pathnames is unlimited.
19274470Sjlemon */
19374470Sjlemon#define MAXGLOBARGS	16384
19474470Sjlemon#
19574470Sjlemon
19674470Sjlemon/*
1971592Srgrimes * Timeout intervals for retrying connections
1981592Srgrimes * to hosts that don't accept PORT cmds.  This
1991592Srgrimes * is a kludge, but given the problems with TCP...
2001592Srgrimes */
2011592Srgrimes#define	SWAITMAX	90	/* wait at most 90 seconds */
2021592Srgrimes#define	SWAITINT	5	/* interval between retries */
2031592Srgrimes
2041592Srgrimesint	swaitmax = SWAITMAX;
2051592Srgrimesint	swaitint = SWAITINT;
2061592Srgrimes
2071592Srgrimes#ifdef SETPROCTITLE
20813139Speter#ifdef OLD_SETPROCTITLE
2091592Srgrimeschar	**Argv = NULL;		/* pointer to argument vector */
2101592Srgrimeschar	*LastArgv = NULL;	/* end of argv */
21113139Speter#endif /* OLD_SETPROCTITLE */
2121592Srgrimeschar	proctitle[LINE_MAX];	/* initial part of title */
2131592Srgrimes#endif /* SETPROCTITLE */
2141592Srgrimes
215137848Syar#define LOGCMD(cmd, file)		logcmd((cmd), (file), NULL, -1)
216137848Syar#define LOGCMD2(cmd, file1, file2)	logcmd((cmd), (file1), (file2), -1)
217137848Syar#define LOGBYTES(cmd, file, cnt)	logcmd((cmd), (file), NULL, (cnt))
2181592Srgrimes
219140472Syarstatic	volatile sig_atomic_t recvurg;
220140472Syarstatic	int transflag;		/* NB: for debugging only */
221140472Syar
222140472Syar#define STARTXFER	flagxfer(1)
223140472Syar#define ENDXFER		flagxfer(0)
224140472Syar
225140472Syar#define START_UNSAFE	maskurg(1)
226140472Syar#define END_UNSAFE	maskurg(0)
227140472Syar
228140472Syar/* It's OK to put an `else' clause after this macro. */
229140472Syar#define CHECKOOB(action)						\
230140472Syar	if (recvurg) {							\
231140472Syar		recvurg = 0;						\
232140472Syar		if (myoob()) {						\
233140472Syar			ENDXFER;					\
234140472Syar			action;						\
235140472Syar		}							\
236140472Syar	}
237140472Syar
23825283Sdavidn#ifdef VIRTUAL_HOSTING
239156156Sumestatic void	 inithosts(int);
240137861Syarstatic void	 selecthost(union sockunion *);
24125283Sdavidn#endif
24290148Simpstatic void	 ack(char *);
24390148Simpstatic void	 sigurg(int);
244140472Syarstatic void	 maskurg(int);
245140472Syarstatic void	 flagxfer(int);
246140472Syarstatic int	 myoob(void);
247216932Scsjpstatic int	 checkuser(char *, char *, int, char **, int *);
24890148Simpstatic FILE	*dataconn(char *, off_t, char *);
24990148Simpstatic void	 dolog(struct sockaddr *);
25090148Simpstatic void	 end_login(void);
25190148Simpstatic FILE	*getdatasock(char *);
252101537Syarstatic int	 guniquefd(char *, char **);
25390148Simpstatic void	 lostconn(int);
25490148Simpstatic void	 sigquit(int);
25590148Simpstatic int	 receive_data(FILE *, FILE *);
256137660Syarstatic int	 send_data(FILE *, FILE *, size_t, off_t, int);
2571592Srgrimesstatic struct passwd *
25890148Simp		 sgetpwnam(char *);
25990148Simpstatic char	*sgetsave(char *);
26090148Simpstatic void	 reapchild(int);
261137851Syarstatic void	 appendf(char **, char *, ...) __printflike(2, 3);
262137848Syarstatic void	 logcmd(char *, char *, char *, off_t);
26390148Simpstatic void      logxfer(char *, off_t, time_t);
264100486Syarstatic char	*doublequote(char *);
265120059Sumestatic int	*socksetup(int, char *, const char *);
2661592Srgrimes
2671592Srgrimesint
26890148Simpmain(int argc, char *argv[], char **envp)
2691592Srgrimes{
270141918Sstefanf	socklen_t addrlen;
271301517Slidl	int ch, on = 1, tos, s = STDIN_FILENO;
2721592Srgrimes	char *cp, line[LINE_MAX];
2731592Srgrimes	FILE *fd;
27456668Sshin	char	*bindname = NULL;
275109742Syar	const char *bindport = "ftp";
27656668Sshin	int	family = AF_UNSPEC;
27789935Syar	struct sigaction sa;
2781592Srgrimes
27936105Sache	tzset();		/* in case no timezone database in ~ftp */
28089935Syar	sigemptyset(&sa.sa_mask);
28189935Syar	sa.sa_flags = SA_RESTART;
28236105Sache
28313139Speter#ifdef OLD_SETPROCTITLE
2841592Srgrimes	/*
2851592Srgrimes	 *  Save start and extent of argv for setproctitle.
2861592Srgrimes	 */
2871592Srgrimes	Argv = argv;
2881592Srgrimes	while (*envp)
2891592Srgrimes		envp++;
2901592Srgrimes	LastArgv = envp[-1] + strlen(envp[-1]);
29113139Speter#endif /* OLD_SETPROCTITLE */
2921592Srgrimes
293138747Syar	/*
294138747Syar	 * Prevent diagnostic messages from appearing on stderr.
295138747Syar	 * We run as a daemon or from inetd; in both cases, there's
296138747Syar	 * more reason in logging to syslog.
297138747Syar	 */
298138747Syar	(void) freopen(_PATH_DEVNULL, "w", stderr);
299138747Syar	opterr = 0;
3006740Sguido
301138747Syar	/*
302138747Syar	 * LOG_NDELAY sets up the logging connection immediately,
303138747Syar	 * necessary for anonymous ftp's that chroot and can't do it later.
304138747Syar	 */
305138747Syar	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
306138747Syar
307110037Syar	while ((ch = getopt(argc, argv,
308168849Syar	                    "468a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) {
3091592Srgrimes		switch (ch) {
310100717Syar		case '4':
311120059Sume			family = (family == AF_INET6) ? AF_UNSPEC : AF_INET;
31215196Sdg			break;
31315196Sdg
314100717Syar		case '6':
315120059Sume			family = (family == AF_INET) ? AF_UNSPEC : AF_INET6;
316100717Syar			break;
317100717Syar
318168849Syar		case '8':
319168849Syar			assumeutf8 = 1;
320168849Syar			break;
321168849Syar
322100717Syar		case 'a':
323100717Syar			bindname = optarg;
324100717Syar			break;
325100717Syar
326100717Syar		case 'A':
327100717Syar			anon_only = 1;
328100717Syar			break;
329100717Syar
3301592Srgrimes		case 'd':
33176096Smarkm			ftpdebug++;
3321592Srgrimes			break;
3331592Srgrimes
334100717Syar		case 'D':
335100717Syar			daemon_mode++;
336100717Syar			break;
337100717Syar
33870102Sphk		case 'E':
33970102Sphk			noepsv = 1;
34070102Sphk			break;
34170102Sphk
342110037Syar		case 'h':
343110037Syar			hostinfo = 0;
344110037Syar			break;
345110037Syar
3461592Srgrimes		case 'l':
3471592Srgrimes			logging++;	/* > 1 == extra logging */
3481592Srgrimes			break;
3491592Srgrimes
350101537Syar		case 'm':
351101537Syar			noguestmod = 0;
352101537Syar			break;
353101537Syar
354100717Syar		case 'M':
355100717Syar			noguestmkd = 1;
356100717Syar			break;
357100717Syar
358100717Syar		case 'o':
359100717Syar			noretr = 1;
360100717Syar			break;
361100717Syar
362100717Syar		case 'O':
363100717Syar			noguestretr = 1;
364100717Syar			break;
365100717Syar
366100717Syar		case 'p':
367100717Syar			pid_file = optarg;
368100717Syar			break;
369100717Syar
370109742Syar		case 'P':
371109742Syar			bindport = optarg;
372109742Syar			break;
373109742Syar
37470102Sphk		case 'r':
37570102Sphk			readonly = 1;
37670102Sphk			break;
37770102Sphk
37817435Spst		case 'R':
37917435Spst			paranoid = 0;
3809933Spst			break;
3819933Spst
3826740Sguido		case 'S':
38317435Spst			stats++;
3846740Sguido			break;
38517435Spst
38617435Spst		case 't':
38717435Spst			timeout = atoi(optarg);
38817435Spst			if (maxtimeout < timeout)
38917435Spst				maxtimeout = timeout;
39017435Spst			break;
39117435Spst
392100717Syar		case 'T':
393100717Syar			maxtimeout = atoi(optarg);
394100717Syar			if (timeout > maxtimeout)
395100717Syar				timeout = maxtimeout;
39617435Spst			break;
39717435Spst
3981592Srgrimes		case 'u':
3991592Srgrimes		    {
4001592Srgrimes			long val = 0;
4011592Srgrimes
4021592Srgrimes			val = strtol(optarg, &optarg, 8);
4031592Srgrimes			if (*optarg != '\0' || val < 0)
404138747Syar				syslog(LOG_WARNING, "bad value for -u");
4051592Srgrimes			else
4061592Srgrimes				defumask = val;
4071592Srgrimes			break;
4081592Srgrimes		    }
409100717Syar		case 'U':
410100717Syar			restricted_data_ports = 0;
41120042Storstenb			break;
4121592Srgrimes
4131592Srgrimes		case 'v':
414100720Syar			ftpdebug++;
4151592Srgrimes			break;
4161592Srgrimes
417102311Syar		case 'W':
418102311Syar			dowtmp = 0;
419102311Syar			break;
420102311Syar
4211592Srgrimes		default:
422138747Syar			syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
4231592Srgrimes			break;
4241592Srgrimes		}
4251592Srgrimes	}
42615196Sdg
42715196Sdg	if (daemon_mode) {
428120059Sume		int *ctl_sock, fd, maxfd = -1, nfds, i;
429120059Sume		fd_set defreadfds, readfds;
430120059Sume		pid_t pid;
431154630Syar		struct pidfh *pfh;
43215196Sdg
433154630Syar		if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) {
434154630Syar			if (errno == EEXIST) {
435154630Syar				syslog(LOG_ERR, "%s already running, pid %d",
436154630Syar				       getprogname(), (int)pid);
437154630Syar				exit(1);
438154630Syar			}
439154630Syar			syslog(LOG_WARNING, "pidfile_open: %m");
440154630Syar		}
441154630Syar
44215196Sdg		/*
44315196Sdg		 * Detach from parent.
44415196Sdg		 */
44515196Sdg		if (daemon(1, 1) < 0) {
44615196Sdg			syslog(LOG_ERR, "failed to become a daemon");
44715196Sdg			exit(1);
44815196Sdg		}
449154630Syar
450154630Syar		if (pfh != NULL && pidfile_write(pfh) == -1)
451154630Syar			syslog(LOG_WARNING, "pidfile_write: %m");
452154630Syar
45389935Syar		sa.sa_handler = reapchild;
45489935Syar		(void)sigaction(SIGCHLD, &sa, NULL);
45556668Sshin
456156156Sume#ifdef VIRTUAL_HOSTING
457156156Sume		inithosts(family);
458156156Sume#endif
459156156Sume
46015196Sdg		/*
46115196Sdg		 * Open a socket, bind it to the FTP port, and start
46215196Sdg		 * listening.
46315196Sdg		 */
464120059Sume		ctl_sock = socksetup(family, bindname, bindport);
465120059Sume		if (ctl_sock == NULL)
46615196Sdg			exit(1);
467120059Sume
468120059Sume		FD_ZERO(&defreadfds);
469120059Sume		for (i = 1; i <= *ctl_sock; i++) {
470120059Sume			FD_SET(ctl_sock[i], &defreadfds);
471120059Sume			if (listen(ctl_sock[i], 32) < 0) {
472120059Sume				syslog(LOG_ERR, "control listen: %m");
473120059Sume				exit(1);
474120059Sume			}
475120059Sume			if (maxfd < ctl_sock[i])
476120059Sume				maxfd = ctl_sock[i];
47715196Sdg		}
478120059Sume
47915196Sdg		/*
48015196Sdg		 * Loop forever accepting connection requests and forking off
48115196Sdg		 * children to handle them.
48215196Sdg		 */
48315196Sdg		while (1) {
484120059Sume			FD_COPY(&defreadfds, &readfds);
485120059Sume			nfds = select(maxfd + 1, &readfds, NULL, NULL, 0);
486120059Sume			if (nfds <= 0) {
487120059Sume				if (nfds < 0 && errno != EINTR)
488120059Sume					syslog(LOG_WARNING, "select: %m");
489120059Sume				continue;
490120059Sume			}
491120059Sume
492120059Sume			pid = -1;
493120059Sume                        for (i = 1; i <= *ctl_sock; i++)
494120059Sume				if (FD_ISSET(ctl_sock[i], &readfds)) {
495120059Sume					addrlen = sizeof(his_addr);
496120059Sume					fd = accept(ctl_sock[i],
497120059Sume					    (struct sockaddr *)&his_addr,
498120059Sume					    &addrlen);
499154634Syar					if (fd == -1) {
500154634Syar						syslog(LOG_WARNING,
501154634Syar						       "accept: %m");
502154634Syar						continue;
503135737Smaxim					}
504154634Syar					switch (pid = fork()) {
505154634Syar					case 0:
506154634Syar						/* child */
507301517Slidl						(void) dup2(fd, s);
508301517Slidl						(void) dup2(fd, STDOUT_FILENO);
509154634Syar						(void) close(fd);
510154634Syar						for (i = 1; i <= *ctl_sock; i++)
511154634Syar							close(ctl_sock[i]);
512154634Syar						if (pfh != NULL)
513154634Syar							pidfile_close(pfh);
514154634Syar						goto gotchild;
515154634Syar					case -1:
516154634Syar						syslog(LOG_WARNING, "fork: %m");
517154634Syar						/* FALLTHROUGH */
518154634Syar					default:
519154634Syar						close(fd);
520154634Syar					}
521120059Sume				}
52215196Sdg		}
52315196Sdg	} else {
52415196Sdg		addrlen = sizeof(his_addr);
525301517Slidl		if (getpeername(s, (struct sockaddr *)&his_addr, &addrlen) < 0) {
52615196Sdg			syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
52715196Sdg			exit(1);
52815196Sdg		}
529156156Sume
530156156Sume#ifdef VIRTUAL_HOSTING
531156156Sume		if (his_addr.su_family == AF_INET6 &&
532156156Sume		    IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
533156156Sume			family = AF_INET;
534156156Sume		else
535156156Sume			family = his_addr.su_family;
536156156Sume		inithosts(family);
537156156Sume#endif
53815196Sdg	}
53915196Sdg
540154634Syargotchild:
54189935Syar	sa.sa_handler = SIG_DFL;
54289935Syar	(void)sigaction(SIGCHLD, &sa, NULL);
5431592Srgrimes
54489935Syar	sa.sa_handler = sigurg;
54589935Syar	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
54689935Syar	(void)sigaction(SIGURG, &sa, NULL);
54789935Syar
54889935Syar	sigfillset(&sa.sa_mask);	/* block all signals in handler */
54989935Syar	sa.sa_flags = SA_RESTART;
55089935Syar	sa.sa_handler = sigquit;
55189935Syar	(void)sigaction(SIGHUP, &sa, NULL);
55289935Syar	(void)sigaction(SIGINT, &sa, NULL);
55389935Syar	(void)sigaction(SIGQUIT, &sa, NULL);
55489935Syar	(void)sigaction(SIGTERM, &sa, NULL);
55589935Syar
55689935Syar	sa.sa_handler = lostconn;
55789935Syar	(void)sigaction(SIGPIPE, &sa, NULL);
55889935Syar
55915196Sdg	addrlen = sizeof(ctrl_addr);
560301517Slidl	if (getsockname(s, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
56115196Sdg		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
56215196Sdg		exit(1);
56315196Sdg	}
564109742Syar	dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */
56525283Sdavidn#ifdef VIRTUAL_HOSTING
56625283Sdavidn	/* select our identity from virtual host table */
56756668Sshin	selecthost(&ctrl_addr);
56825283Sdavidn#endif
56915196Sdg#ifdef IP_TOS
57056668Sshin	if (ctrl_addr.su_family == AF_INET)
57156668Sshin      {
57215196Sdg	tos = IPTOS_LOWDELAY;
573301517Slidl	if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
574100609Syar		syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m");
57556668Sshin      }
57615196Sdg#endif
57735482Sdg	/*
57835482Sdg	 * Disable Nagle on the control channel so that we don't have to wait
57935482Sdg	 * for peer's ACK before issuing our next reply.
58035482Sdg	 */
581301517Slidl	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
582100609Syar		syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m");
58335482Sdg
58456668Sshin	data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
58515196Sdg
586202209Sed	(void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid());
58717435Spst
5881592Srgrimes	/* Try to handle urgent data inline */
5891592Srgrimes#ifdef SO_OOBINLINE
590301517Slidl	if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0)
591100609Syar		syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m");
5921592Srgrimes#endif
5931592Srgrimes
5941592Srgrimes#ifdef	F_SETOWN
595301517Slidl	if (fcntl(s, F_SETOWN, getpid()) == -1)
5961592Srgrimes		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
5971592Srgrimes#endif
59856668Sshin	dolog((struct sockaddr *)&his_addr);
5991592Srgrimes	/*
6001592Srgrimes	 * Set up default state
6011592Srgrimes	 */
6021592Srgrimes	data = -1;
6031592Srgrimes	type = TYPE_A;
6041592Srgrimes	form = FORM_N;
6051592Srgrimes	stru = STRU_F;
6061592Srgrimes	mode = MODE_S;
6071592Srgrimes	tmpline[0] = '\0';
6081592Srgrimes
6091592Srgrimes	/* If logins are disabled, print out the message. */
6101592Srgrimes	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
6111592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
6121592Srgrimes			if ((cp = strchr(line, '\n')) != NULL)
6131592Srgrimes				*cp = '\0';
6141592Srgrimes			lreply(530, "%s", line);
6151592Srgrimes		}
6161592Srgrimes		(void) fflush(stdout);
6171592Srgrimes		(void) fclose(fd);
6181592Srgrimes		reply(530, "System not available.");
6191592Srgrimes		exit(0);
6201592Srgrimes	}
62125283Sdavidn#ifdef VIRTUAL_HOSTING
622130428Sobrien	fd = fopen(thishost->welcome, "r");
62325283Sdavidn#else
624130428Sobrien	fd = fopen(_PATH_FTPWELCOME, "r");
62525283Sdavidn#endif
626130428Sobrien	if (fd != NULL) {
6271592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
6281592Srgrimes			if ((cp = strchr(line, '\n')) != NULL)
6291592Srgrimes				*cp = '\0';
6301592Srgrimes			lreply(220, "%s", line);
6311592Srgrimes		}
6321592Srgrimes		(void) fflush(stdout);
6331592Srgrimes		(void) fclose(fd);
6341592Srgrimes		/* reply(220,) must follow */
6351592Srgrimes	}
63625283Sdavidn#ifndef VIRTUAL_HOSTING
63727650Sdavidn	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
63876096Smarkm		fatalerror("Ran out of memory.");
639137983Syar	if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0)
640137983Syar		hostname[0] = '\0';
64145422Sbrian	hostname[MAXHOSTNAMELEN - 1] = '\0';
64225283Sdavidn#endif
643110037Syar	if (hostinfo)
644110037Syar		reply(220, "%s FTP server (%s) ready.", hostname, version);
645110037Syar	else
646110037Syar		reply(220, "FTP server ready.");
647301241Slidl#ifdef USE_BLACKLIST
648301241Slidl	blacklist_init();
649301241Slidl#endif
6501592Srgrimes	for (;;)
6511592Srgrimes		(void) yyparse();
6521592Srgrimes	/* NOTREACHED */
6531592Srgrimes}
6541592Srgrimes
6551592Srgrimesstatic void
65690148Simplostconn(int signo)
6571592Srgrimes{
6581592Srgrimes
65976096Smarkm	if (ftpdebug)
6601592Srgrimes		syslog(LOG_DEBUG, "lost connection");
66131329Scharnier	dologout(1);
6621592Srgrimes}
6631592Srgrimes
66489935Syarstatic void
66590148Simpsigquit(int signo)
66689935Syar{
66789935Syar
66889935Syar	syslog(LOG_ERR, "got signal %d", signo);
66989935Syar	dologout(1);
67089935Syar}
67189935Syar
67225283Sdavidn#ifdef VIRTUAL_HOSTING
6731592Srgrimes/*
67425283Sdavidn * read in virtual host tables (if they exist)
67525283Sdavidn */
67625283Sdavidn
67725283Sdavidnstatic void
678156156Sumeinithosts(int family)
67925283Sdavidn{
680100182Syar	int insert;
68199877Syar	size_t len;
68225283Sdavidn	FILE *fp;
68399877Syar	char *cp, *mp, *line;
68499877Syar	char *hostname;
685100182Syar	char *vhost, *anonuser, *statfile, *welcome, *loginmsg;
68625283Sdavidn	struct ftphost *hrp, *lhrp;
68756668Sshin	struct addrinfo hints, *res, *ai;
68825283Sdavidn
68925283Sdavidn	/*
69025283Sdavidn	 * Fill in the default host information
69125283Sdavidn	 */
69299877Syar	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
69376096Smarkm		fatalerror("Ran out of memory.");
694137983Syar	if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0)
69599877Syar		hostname[0] = '\0';
69699877Syar	hostname[MAXHOSTNAMELEN - 1] = '\0';
69799877Syar	if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
69899877Syar		fatalerror("Ran out of memory.");
69999877Syar	hrp->hostname = hostname;
70057124Sshin	hrp->hostinfo = NULL;
70156668Sshin
70256668Sshin	memset(&hints, 0, sizeof(hints));
703156156Sume	hints.ai_flags = AI_PASSIVE;
704156156Sume	hints.ai_family = family;
705156156Sume	hints.ai_socktype = SOCK_STREAM;
706102183Syar	if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0)
70757124Sshin		hrp->hostinfo = res;
70825283Sdavidn	hrp->statfile = _PATH_FTPDSTATFILE;
70925283Sdavidn	hrp->welcome  = _PATH_FTPWELCOME;
71025283Sdavidn	hrp->loginmsg = _PATH_FTPLOGINMESG;
71125283Sdavidn	hrp->anonuser = "ftp";
71225283Sdavidn	hrp->next = NULL;
71325283Sdavidn	thishost = firsthost = lhrp = hrp;
71425283Sdavidn	if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
715102474Syar		int addrsize, gothost;
71656668Sshin		void *addr;
71756668Sshin		struct hostent *hp;
71856668Sshin
71999877Syar		while ((line = fgetln(fp, &len)) != NULL) {
72056668Sshin			int	i, hp_error;
72125283Sdavidn
72299877Syar			/* skip comments */
72399877Syar			if (line[0] == '#')
72425283Sdavidn				continue;
72599877Syar			if (line[len - 1] == '\n') {
72699877Syar				line[len - 1] = '\0';
72799877Syar				mp = NULL;
72899877Syar			} else {
72999877Syar				if ((mp = malloc(len + 1)) == NULL)
73099877Syar					fatalerror("Ran out of memory.");
73199877Syar				memcpy(mp, line, len);
73299877Syar				mp[len] = '\0';
73399877Syar				line = mp;
73425283Sdavidn			}
73525283Sdavidn			cp = strtok(line, " \t");
73699877Syar			/* skip empty lines */
73799877Syar			if (cp == NULL)
73899877Syar				goto nextline;
739100182Syar			vhost = cp;
74056668Sshin
741100182Syar			/* set defaults */
742100182Syar			anonuser = "ftp";
743100182Syar			statfile = _PATH_FTPDSTATFILE;
744100182Syar			welcome  = _PATH_FTPWELCOME;
745100182Syar			loginmsg = _PATH_FTPLOGINMESG;
746100182Syar
747100182Syar			/*
748100182Syar			 * Preparse the line so we can use its info
749100182Syar			 * for all the addresses associated with
750100182Syar			 * the virtual host name.
751100182Syar			 * Field 0, the virtual host name, is special:
752100182Syar			 * it's already parsed off and will be strdup'ed
753100182Syar			 * later, after we know its canonical form.
754100182Syar			 */
755100182Syar			for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++)
756100182Syar				if (*cp != '-' && (cp = strdup(cp)))
757100182Syar					switch (i) {
758100182Syar					case 1:	/* anon user permissions */
759100182Syar						anonuser = cp;
760100182Syar						break;
761100182Syar					case 2: /* statistics file */
762100182Syar						statfile = cp;
763100182Syar						break;
764100182Syar					case 3: /* welcome message */
765100182Syar						welcome  = cp;
766100182Syar						break;
767100182Syar					case 4: /* login message */
768100182Syar						loginmsg = cp;
769100182Syar						break;
770100182Syar					default: /* programming error */
771100182Syar						abort();
772100182Syar						/* NOTREACHED */
773100182Syar					}
774100182Syar
77556668Sshin			hints.ai_flags = AI_PASSIVE;
776156156Sume			hints.ai_family = family;
777156156Sume			hints.ai_socktype = SOCK_STREAM;
778102183Syar			if (getaddrinfo(vhost, NULL, &hints, &res) != 0)
77999877Syar				goto nextline;
78056668Sshin			for (ai = res; ai != NULL && ai->ai_addr != NULL;
78162100Sdavidn			     ai = ai->ai_next) {
78256668Sshin
78362100Sdavidn			gothost = 0;
78425283Sdavidn			for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
78557124Sshin				struct addrinfo *hi;
78657124Sshin
78757124Sshin				for (hi = hrp->hostinfo; hi != NULL;
78857124Sshin				     hi = hi->ai_next)
78957124Sshin					if (hi->ai_addrlen == ai->ai_addrlen &&
79057124Sshin					    memcmp(hi->ai_addr,
79157124Sshin						   ai->ai_addr,
79262100Sdavidn						   ai->ai_addr->sa_len) == 0) {
79362100Sdavidn						gothost++;
79457124Sshin						break;
795100183Syar					}
79662100Sdavidn				if (gothost)
79762100Sdavidn					break;
79825283Sdavidn			}
79925283Sdavidn			if (hrp == NULL) {
80025283Sdavidn				if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
80199877Syar					goto nextline;
802102183Syar				hrp->hostname = NULL;
803100182Syar				insert = 1;
804102473Syar			} else {
805106754Syar				if (hrp->hostinfo && hrp->hostinfo != res)
806102473Syar					freeaddrinfo(hrp->hostinfo);
807100182Syar				insert = 0; /* host already in the chain */
808102473Syar			}
80957124Sshin			hrp->hostinfo = res;
81057124Sshin
81125283Sdavidn			/*
81225283Sdavidn			 * determine hostname to use.
81356668Sshin			 * force defined name if there is a valid alias
81425283Sdavidn			 * otherwise fallback to primary hostname
81525283Sdavidn			 */
81656668Sshin			/* XXX: getaddrinfo() can't do alias check */
81757124Sshin			switch(hrp->hostinfo->ai_family) {
81856668Sshin			case AF_INET:
819100259Syar				addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr;
820100259Syar				addrsize = sizeof(struct in_addr);
82156668Sshin				break;
82256668Sshin			case AF_INET6:
823100259Syar				addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr;
824100259Syar				addrsize = sizeof(struct in6_addr);
82556668Sshin				break;
82656668Sshin			default:
82756668Sshin				/* should not reach here */
828102473Syar				freeaddrinfo(hrp->hostinfo);
829102473Syar				if (insert)
830102473Syar					free(hrp); /*not in chain, can free*/
831102473Syar				else
832102473Syar					hrp->hostinfo = NULL; /*mark as blank*/
83399877Syar				goto nextline;
83456668Sshin				/* NOTREACHED */
83556668Sshin			}
836100612Syar			if ((hp = getipnodebyaddr(addr, addrsize,
83757124Sshin						  hrp->hostinfo->ai_family,
83856668Sshin						  &hp_error)) != NULL) {
839100182Syar				if (strcmp(vhost, hp->h_name) != 0) {
84025283Sdavidn					if (hp->h_aliases == NULL)
841100182Syar						vhost = hp->h_name;
84225283Sdavidn					else {
84325283Sdavidn						i = 0;
84425283Sdavidn						while (hp->h_aliases[i] &&
845100182Syar						       strcmp(vhost, hp->h_aliases[i]) != 0)
84625283Sdavidn							++i;
84725283Sdavidn						if (hp->h_aliases[i] == NULL)
848100182Syar							vhost = hp->h_name;
84925283Sdavidn					}
85025283Sdavidn				}
85125283Sdavidn			}
852102183Syar			if (hrp->hostname &&
853102183Syar			    strcmp(hrp->hostname, vhost) != 0) {
854102183Syar				free(hrp->hostname);
855102183Syar				hrp->hostname = NULL;
856102183Syar			}
857102183Syar			if (hrp->hostname == NULL &&
858102473Syar			    (hrp->hostname = strdup(vhost)) == NULL) {
859102473Syar				freeaddrinfo(hrp->hostinfo);
860102473Syar				hrp->hostinfo = NULL; /* mark as blank */
861102473Syar				if (hp)
862102473Syar					freehostent(hp);
863100182Syar				goto nextline;
864102473Syar			}
865100182Syar			hrp->anonuser = anonuser;
866100182Syar			hrp->statfile = statfile;
867100182Syar			hrp->welcome  = welcome;
868100182Syar			hrp->loginmsg = loginmsg;
869100182Syar			if (insert) {
870100182Syar				hrp->next  = NULL;
871100182Syar				lhrp->next = hrp;
872100182Syar				lhrp = hrp;
873100182Syar			}
874100263Syar			if (hp)
875100263Syar				freehostent(hp);
87656668Sshin		      }
87799877Syarnextline:
87899877Syar			if (mp)
87999877Syar				free(mp);
88025283Sdavidn		}
88125283Sdavidn		(void) fclose(fp);
88225283Sdavidn	}
88325283Sdavidn}
88425283Sdavidn
88525283Sdavidnstatic void
88690148Simpselecthost(union sockunion *su)
88725283Sdavidn{
88825283Sdavidn	struct ftphost	*hrp;
88956668Sshin	u_int16_t port;
89056668Sshin#ifdef INET6
89156668Sshin	struct in6_addr *mapped_in6 = NULL;
89256668Sshin#endif
89357124Sshin	struct addrinfo *hi;
89425283Sdavidn
89556668Sshin#ifdef INET6
89656668Sshin	/*
89756668Sshin	 * XXX IPv4 mapped IPv6 addr consideraton,
89856668Sshin	 * specified in rfc2373.
89956668Sshin	 */
90056668Sshin	if (su->su_family == AF_INET6 &&
90156668Sshin	    IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
90256668Sshin		mapped_in6 = &su->su_sin6.sin6_addr;
90356668Sshin#endif
90456668Sshin
90525283Sdavidn	hrp = thishost = firsthost;	/* default */
90656668Sshin	port = su->su_port;
90756668Sshin	su->su_port = 0;
90825283Sdavidn	while (hrp != NULL) {
90962100Sdavidn	    for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) {
91057124Sshin		if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) {
91125283Sdavidn			thishost = hrp;
912137987Syar			goto found;
91325283Sdavidn		}
91456668Sshin#ifdef INET6
91556668Sshin		/* XXX IPv4 mapped IPv6 addr consideraton */
91657124Sshin		if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL &&
91756668Sshin		    (memcmp(&mapped_in6->s6_addr[12],
91857124Sshin			    &((struct sockaddr_in *)hi->ai_addr)->sin_addr,
91956668Sshin			    sizeof(struct in_addr)) == 0)) {
92056668Sshin			thishost = hrp;
921137987Syar			goto found;
92256668Sshin		}
92356668Sshin#endif
92462100Sdavidn	    }
92562100Sdavidn	    hrp = hrp->next;
92625283Sdavidn	}
927137987Syarfound:
92856668Sshin	su->su_port = port;
92925283Sdavidn	/* setup static variables as appropriate */
93025283Sdavidn	hostname = thishost->hostname;
93125283Sdavidn	ftpuser = thishost->anonuser;
93225283Sdavidn}
93325283Sdavidn#endif
93425283Sdavidn
93525283Sdavidn/*
9361592Srgrimes * Helper function for sgetpwnam().
9371592Srgrimes */
9381592Srgrimesstatic char *
93990148Simpsgetsave(char *s)
9401592Srgrimes{
941137659Syar	char *new = malloc(strlen(s) + 1);
9421592Srgrimes
9431592Srgrimes	if (new == NULL) {
944137852Syar		reply(421, "Ran out of memory.");
9451592Srgrimes		dologout(1);
9461592Srgrimes		/* NOTREACHED */
9471592Srgrimes	}
9481592Srgrimes	(void) strcpy(new, s);
9491592Srgrimes	return (new);
9501592Srgrimes}
9511592Srgrimes
9521592Srgrimes/*
9531592Srgrimes * Save the result of a getpwnam.  Used for USER command, since
9541592Srgrimes * the data returned must not be clobbered by any other command
9551592Srgrimes * (e.g., globbing).
956137076Syar * NB: The data returned by sgetpwnam() will remain valid until
957137076Syar * the next call to this function.  Its difference from getpwnam()
958137076Syar * is that sgetpwnam() is known to be called from ftpd code only.
9591592Srgrimes */
9601592Srgrimesstatic struct passwd *
96190148Simpsgetpwnam(char *name)
9621592Srgrimes{
9631592Srgrimes	static struct passwd save;
9641592Srgrimes	struct passwd *p;
9651592Srgrimes
9661592Srgrimes	if ((p = getpwnam(name)) == NULL)
9671592Srgrimes		return (p);
9681592Srgrimes	if (save.pw_name) {
9691592Srgrimes		free(save.pw_name);
9701592Srgrimes		free(save.pw_passwd);
971261885Sbrueffer		free(save.pw_class);
9721592Srgrimes		free(save.pw_gecos);
9731592Srgrimes		free(save.pw_dir);
9741592Srgrimes		free(save.pw_shell);
9751592Srgrimes	}
9761592Srgrimes	save = *p;
9771592Srgrimes	save.pw_name = sgetsave(p->pw_name);
9781592Srgrimes	save.pw_passwd = sgetsave(p->pw_passwd);
979261885Sbrueffer	save.pw_class = sgetsave(p->pw_class);
9801592Srgrimes	save.pw_gecos = sgetsave(p->pw_gecos);
9811592Srgrimes	save.pw_dir = sgetsave(p->pw_dir);
9821592Srgrimes	save.pw_shell = sgetsave(p->pw_shell);
9831592Srgrimes	return (&save);
9841592Srgrimes}
9851592Srgrimes
9861592Srgrimesstatic int login_attempts;	/* number of failed login attempts */
9871592Srgrimesstatic int askpasswd;		/* had user command, ask for passwd */
98864778Ssheldonhstatic char curname[MAXLOGNAME];	/* current USER name */
9891592Srgrimes
9901592Srgrimes/*
9911592Srgrimes * USER command.
9921592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable;
9931592Srgrimes * sets askpasswd if a PASS command is expected.  If logged in previously,
9941592Srgrimes * need to reset state.  If name is "ftp" or "anonymous", the name is not in
9951592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
9961592Srgrimes * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
9971592Srgrimes * requesting login privileges.  Disallow anyone who does not have a standard
9981592Srgrimes * shell as returned by getusershell().  Disallow anyone mentioned in the file
9991592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
10001592Srgrimes */
10011592Srgrimesvoid
100290148Simpuser(char *name)
10031592Srgrimes{
1004216932Scsjp	int ecode;
10051592Srgrimes	char *cp, *shell;
10061592Srgrimes
10071592Srgrimes	if (logged_in) {
10081592Srgrimes		if (guest) {
10091592Srgrimes			reply(530, "Can't change user from guest login.");
10101592Srgrimes			return;
101117435Spst		} else if (dochroot) {
101217435Spst			reply(530, "Can't change user from chroot user.");
101317435Spst			return;
10141592Srgrimes		}
10151592Srgrimes		end_login();
10161592Srgrimes	}
10171592Srgrimes
10181592Srgrimes	guest = 0;
1019130428Sobrien#ifdef VIRTUAL_HOSTING
1020130428Sobrien	pw = sgetpwnam(thishost->anonuser);
1021130428Sobrien#else
1022130428Sobrien	pw = sgetpwnam("ftp");
1023130428Sobrien#endif
10241592Srgrimes	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
1025216932Scsjp		if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) ||
1026216932Scsjp		    (ecode != 0 && ecode != ENOENT))
10271592Srgrimes			reply(530, "User %s access denied.", name);
1028216932Scsjp		else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) ||
1029216932Scsjp		    (ecode != 0 && ecode != ENOENT))
1030216932Scsjp			reply(530, "User %s access denied.", name);
1031130428Sobrien		else if (pw != NULL) {
10321592Srgrimes			guest = 1;
10331592Srgrimes			askpasswd = 1;
10341592Srgrimes			reply(331,
10353938Spst			"Guest login ok, send your email address as password.");
10361592Srgrimes		} else
10371592Srgrimes			reply(530, "User %s unknown.", name);
10381592Srgrimes		if (!askpasswd && logging)
10391592Srgrimes			syslog(LOG_NOTICE,
10401592Srgrimes			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
10411592Srgrimes		return;
10421592Srgrimes	}
104320042Storstenb	if (anon_only != 0) {
104420042Storstenb		reply(530, "Sorry, only anonymous ftp allowed.");
104520042Storstenb		return;
104620042Storstenb	}
104720042Storstenb
104817478Smarkm	if ((pw = sgetpwnam(name))) {
10491592Srgrimes		if ((shell = pw->pw_shell) == NULL || *shell == 0)
10501592Srgrimes			shell = _PATH_BSHELL;
1051124687Scharnier		setusershell();
10521592Srgrimes		while ((cp = getusershell()) != NULL)
10531592Srgrimes			if (strcmp(cp, shell) == 0)
10541592Srgrimes				break;
10551592Srgrimes		endusershell();
10561592Srgrimes
1057216932Scsjp		if (cp == NULL ||
1058216932Scsjp		    (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) ||
1059216932Scsjp		    (ecode != 0 && ecode != ENOENT))) {
10601592Srgrimes			reply(530, "User %s access denied.", name);
10611592Srgrimes			if (logging)
10621592Srgrimes				syslog(LOG_NOTICE,
10631592Srgrimes				    "FTP LOGIN REFUSED FROM %s, %s",
10641592Srgrimes				    remotehost, name);
1065137811Syar			pw = NULL;
10661592Srgrimes			return;
10671592Srgrimes		}
10681592Srgrimes	}
10691592Srgrimes	if (logging)
10701592Srgrimes		strncpy(curname, name, sizeof(curname)-1);
107188763Sache
107288763Sache	pwok = 0;
107379469Smarkm#ifdef USE_PAM
107479469Smarkm	/* XXX Kluge! The conversation mechanism needs to be fixed. */
107588763Sache#endif
107688763Sache	if (opiechallenge(&opiedata, name, opieprompt) == 0) {
107788763Sache		pwok = (pw != NULL) &&
107888763Sache		       opieaccessfile(remotehost) &&
107988763Sache		       opiealways(pw->pw_dir);
108088763Sache		reply(331, "Response to %s %s for %s.",
108188763Sache		      opieprompt, pwok ? "requested" : "required", name);
108288763Sache	} else {
108388763Sache		pwok = 1;
108484146Sache		reply(331, "Password required for %s.", name);
108588763Sache	}
10861592Srgrimes	askpasswd = 1;
10871592Srgrimes	/*
10881592Srgrimes	 * Delay before reading passwd after first failed
10891592Srgrimes	 * attempt to slow down passwd-guessing programs.
10901592Srgrimes	 */
10911592Srgrimes	if (login_attempts)
1092137659Syar		sleep(login_attempts);
10931592Srgrimes}
10941592Srgrimes
10951592Srgrimes/*
1096109893Syar * Check if a user is in the file "fname",
1097109893Syar * return a pointer to a malloc'd string with the rest
1098109893Syar * of the matching line in "residue" if not NULL.
10991592Srgrimes */
11001592Srgrimesstatic int
1101216932Scsjpcheckuser(char *fname, char *name, int pwset, char **residue, int *ecode)
11021592Srgrimes{
11031592Srgrimes	FILE *fd;
11041592Srgrimes	int found = 0;
110599877Syar	size_t len;
110699877Syar	char *line, *mp, *p;
11071592Srgrimes
1108216932Scsjp	if (ecode != NULL)
1109216932Scsjp		*ecode = 0;
111017435Spst	if ((fd = fopen(fname, "r")) != NULL) {
111199877Syar		while (!found && (line = fgetln(fd, &len)) != NULL) {
111299877Syar			/* skip comments */
111399877Syar			if (line[0] == '#')
111499877Syar				continue;
111599877Syar			if (line[len - 1] == '\n') {
111699877Syar				line[len - 1] = '\0';
111799877Syar				mp = NULL;
111899877Syar			} else {
111999877Syar				if ((mp = malloc(len + 1)) == NULL)
112099877Syar					fatalerror("Ran out of memory.");
112199877Syar				memcpy(mp, line, len);
112299877Syar				mp[len] = '\0';
112399877Syar				line = mp;
112499877Syar			}
112599877Syar			/* avoid possible leading and trailing whitespace */
112699877Syar			p = strtok(line, " \t");
112799877Syar			/* skip empty lines */
112899877Syar			if (p == NULL)
112999877Syar				goto nextline;
113099877Syar			/*
113199877Syar			 * if first chr is '@', check group membership
113299877Syar			 */
113399877Syar			if (p[0] == '@') {
113499877Syar				int i = 0;
113599877Syar				struct group *grp;
113699877Syar
1137109893Syar				if (p[1] == '\0') /* single @ matches anyone */
113899877Syar					found = 1;
1139109893Syar				else {
1140109893Syar					if ((grp = getgrnam(p+1)) == NULL)
1141109893Syar						goto nextline;
1142109893Syar					/*
1143109893Syar					 * Check user's default group
1144109893Syar					 */
1145109893Syar					if (pwset && grp->gr_gid == pw->pw_gid)
1146109893Syar						found = 1;
1147109893Syar					/*
1148109893Syar					 * Check supplementary groups
1149109893Syar					 */
1150109893Syar					while (!found && grp->gr_mem[i])
1151109893Syar						found = strcmp(name,
1152109893Syar							grp->gr_mem[i++])
1153109893Syar							== 0;
1154109893Syar				}
11551592Srgrimes			}
115699877Syar			/*
115799877Syar			 * Otherwise, just check for username match
115899877Syar			 */
115999877Syar			else
116099877Syar				found = strcmp(p, name) == 0;
1161109893Syar			/*
1162109893Syar			 * Save the rest of line to "residue" if matched
1163109893Syar			 */
1164109893Syar			if (found && residue) {
1165109938Syar				if ((p = strtok(NULL, "")) != NULL)
1166109938Syar					p += strspn(p, " \t");
1167109938Syar				if (p && *p) {
1168109893Syar				 	if ((*residue = strdup(p)) == NULL)
1169109893Syar						fatalerror("Ran out of memory.");
1170109893Syar				} else
1171109893Syar					*residue = NULL;
1172109893Syar			}
117399877Syarnextline:
117499877Syar			if (mp)
117599877Syar				free(mp);
117699877Syar		}
11771592Srgrimes		(void) fclose(fd);
1178216932Scsjp	} else if (ecode != NULL)
1179216932Scsjp		*ecode = errno;
11801592Srgrimes	return (found);
11811592Srgrimes}
11821592Srgrimes
11831592Srgrimes/*
11841592Srgrimes * Terminate login as previous user, if any, resetting state;
11851592Srgrimes * used when USER command is given or login fails.
11861592Srgrimes */
11871592Srgrimesstatic void
118890148Simpend_login(void)
11891592Srgrimes{
119074874Smarkm#ifdef USE_PAM
119174874Smarkm	int e;
119274874Smarkm#endif
11931592Srgrimes
1194132893Syar	(void) seteuid(0);
1195202604Sed	if (logged_in && dowtmp)
1196202604Sed		ftpd_logwtmp(wtmpid, NULL, NULL);
11971592Srgrimes	pw = NULL;
119825101Sdavidn#ifdef	LOGIN_CAP
1199223434Strasz	setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN |
1200223434Strasz		       LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH |
1201223434Strasz		       LOGIN_SETENV));
120225101Sdavidn#endif
120374874Smarkm#ifdef USE_PAM
1204137078Syar	if (pamh) {
1205137078Syar		if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
1206137078Syar			syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
1207137078Syar		if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
1208137078Syar			syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e));
1209137078Syar		if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
1210137078Syar			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1211137078Syar		pamh = NULL;
1212137078Syar	}
121374874Smarkm#endif
12141592Srgrimes	logged_in = 0;
12151592Srgrimes	guest = 0;
121617435Spst	dochroot = 0;
12171592Srgrimes}
12181592Srgrimes
121974874Smarkm#ifdef USE_PAM
122051433Smarkm
122151433Smarkm/*
122251433Smarkm * the following code is stolen from imap-uw PAM authentication module and
122351433Smarkm * login.c
122451433Smarkm */
122551433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL)
122651433Smarkm
122751433Smarkmstruct cred_t {
122851433Smarkm	const char *uname;		/* user name */
122951433Smarkm	const char *pass;		/* password */
123051433Smarkm};
123151433Smarkmtypedef struct cred_t cred_t;
123251433Smarkm
123351433Smarkmstatic int
123451433Smarkmauth_conv(int num_msg, const struct pam_message **msg,
123551433Smarkm	  struct pam_response **resp, void *appdata)
123651433Smarkm{
123751433Smarkm	int i;
123851433Smarkm	cred_t *cred = (cred_t *) appdata;
123991244Sdes	struct pam_response *reply;
124051433Smarkm
124191244Sdes	reply = calloc(num_msg, sizeof *reply);
124291244Sdes	if (reply == NULL)
124391244Sdes		return PAM_BUF_ERR;
124491244Sdes
124551433Smarkm	for (i = 0; i < num_msg; i++) {
124651433Smarkm		switch (msg[i]->msg_style) {
124751433Smarkm		case PAM_PROMPT_ECHO_ON:	/* assume want user name */
124851433Smarkm			reply[i].resp_retcode = PAM_SUCCESS;
124951433Smarkm			reply[i].resp = COPY_STRING(cred->uname);
125051433Smarkm			/* PAM frees resp. */
125151433Smarkm			break;
125251433Smarkm		case PAM_PROMPT_ECHO_OFF:	/* assume want password */
125351433Smarkm			reply[i].resp_retcode = PAM_SUCCESS;
125451433Smarkm			reply[i].resp = COPY_STRING(cred->pass);
125551433Smarkm			/* PAM frees resp. */
125651433Smarkm			break;
125751433Smarkm		case PAM_TEXT_INFO:
125851433Smarkm		case PAM_ERROR_MSG:
125951433Smarkm			reply[i].resp_retcode = PAM_SUCCESS;
126051433Smarkm			reply[i].resp = NULL;
126151433Smarkm			break;
126251433Smarkm		default:			/* unknown message style */
126351433Smarkm			free(reply);
126451433Smarkm			return PAM_CONV_ERR;
126551433Smarkm		}
126651433Smarkm	}
126751433Smarkm
126851433Smarkm	*resp = reply;
126951433Smarkm	return PAM_SUCCESS;
127051433Smarkm}
127151433Smarkm
127251433Smarkm/*
127351433Smarkm * Attempt to authenticate the user using PAM.  Returns 0 if the user is
127451433Smarkm * authenticated, or 1 if not authenticated.  If some sort of PAM system
127551433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
127651433Smarkm * function returns -1.  This can be used as an indication that we should
127751433Smarkm * fall back to a different authentication mechanism.
127851433Smarkm */
127951433Smarkmstatic int
128051433Smarkmauth_pam(struct passwd **ppw, const char *pass)
128151433Smarkm{
128251433Smarkm	const char *tmpl_user;
128351433Smarkm	const void *item;
128451433Smarkm	int rval;
128551433Smarkm	int e;
128651433Smarkm	cred_t auth_cred = { (*ppw)->pw_name, pass };
128751433Smarkm	struct pam_conv conv = { &auth_conv, &auth_cred };
128851433Smarkm
128951433Smarkm	e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh);
129051433Smarkm	if (e != PAM_SUCCESS) {
1291137108Syar		/*
1292137108Syar		 * In OpenPAM, it's OK to pass NULL to pam_strerror()
1293137108Syar		 * if context creation has failed in the first place.
1294137108Syar		 */
1295137108Syar		syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e));
129651433Smarkm		return -1;
129751433Smarkm	}
129851433Smarkm
129967007Sguido	e = pam_set_item(pamh, PAM_RHOST, remotehost);
130067007Sguido	if (e != PAM_SUCCESS) {
130167007Sguido		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
130267007Sguido			pam_strerror(pamh, e));
1303137078Syar		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
1304137078Syar			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1305137078Syar		}
1306137078Syar		pamh = NULL;
130767007Sguido		return -1;
130867007Sguido	}
130967007Sguido
131051433Smarkm	e = pam_authenticate(pamh, 0);
131151433Smarkm	switch (e) {
131251433Smarkm	case PAM_SUCCESS:
131351433Smarkm		/*
131451433Smarkm		 * With PAM we support the concept of a "template"
131551433Smarkm		 * user.  The user enters a login name which is
131651433Smarkm		 * authenticated by PAM, usually via a remote service
131751433Smarkm		 * such as RADIUS or TACACS+.  If authentication
131851433Smarkm		 * succeeds, a different but related "template" name
131951433Smarkm		 * is used for setting the credentials, shell, and
132051433Smarkm		 * home directory.  The name the user enters need only
132151433Smarkm		 * exist on the remote authentication server, but the
132251433Smarkm		 * template name must be present in the local password
132351433Smarkm		 * database.
132451433Smarkm		 *
132551433Smarkm		 * This is supported by two various mechanisms in the
132651433Smarkm		 * individual modules.  However, from the application's
132751433Smarkm		 * point of view, the template user is always passed
132851433Smarkm		 * back as a changed value of the PAM_USER item.
132951433Smarkm		 */
133051433Smarkm		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
133151433Smarkm		    PAM_SUCCESS) {
133251433Smarkm			tmpl_user = (const char *) item;
133351433Smarkm			if (strcmp((*ppw)->pw_name, tmpl_user) != 0)
133451433Smarkm				*ppw = getpwnam(tmpl_user);
133551433Smarkm		} else
133651433Smarkm			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
133751433Smarkm			    pam_strerror(pamh, e));
133851433Smarkm		rval = 0;
133951433Smarkm		break;
134051433Smarkm
134151433Smarkm	case PAM_AUTH_ERR:
134251433Smarkm	case PAM_USER_UNKNOWN:
134351433Smarkm	case PAM_MAXTRIES:
134451433Smarkm		rval = 1;
134551433Smarkm		break;
134651433Smarkm
134751433Smarkm	default:
134874874Smarkm		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
134951433Smarkm		rval = -1;
135051433Smarkm		break;
135151433Smarkm	}
135251433Smarkm
135374874Smarkm	if (rval == 0) {
135474874Smarkm		e = pam_acct_mgmt(pamh, 0);
1355137986Syar		if (e != PAM_SUCCESS) {
1356137986Syar			syslog(LOG_ERR, "pam_acct_mgmt: %s",
1357137986Syar						pam_strerror(pamh, e));
135874874Smarkm			rval = 1;
135974874Smarkm		}
136051433Smarkm	}
136174874Smarkm
136274874Smarkm	if (rval != 0) {
136374874Smarkm		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
136474874Smarkm			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
136574874Smarkm		}
136674874Smarkm		pamh = NULL;
136774874Smarkm	}
136851433Smarkm	return rval;
136951433Smarkm}
137051433Smarkm
137174874Smarkm#endif /* USE_PAM */
137251433Smarkm
13731592Srgrimesvoid
137490148Simppass(char *passwd)
13751592Srgrimes{
1376216932Scsjp	int rval, ecode;
13771592Srgrimes	FILE *fd;
137825101Sdavidn#ifdef	LOGIN_CAP
137925101Sdavidn	login_cap_t *lc = NULL;
138025101Sdavidn#endif
138174874Smarkm#ifdef USE_PAM
138274874Smarkm	int e;
138374874Smarkm#endif
1384109939Syar	char *residue = NULL;
138588763Sache	char *xpasswd;
13861592Srgrimes
13871592Srgrimes	if (logged_in || askpasswd == 0) {
13881592Srgrimes		reply(503, "Login with USER first.");
13891592Srgrimes		return;
13901592Srgrimes	}
13911592Srgrimes	askpasswd = 0;
13921592Srgrimes	if (!guest) {		/* "ftp" is only account allowed no password */
139317435Spst		if (pw == NULL) {
139417435Spst			rval = 1;	/* failure below */
139517435Spst			goto skip;
139617435Spst		}
139774874Smarkm#ifdef USE_PAM
139851433Smarkm		rval = auth_pam(&pw, passwd);
139989622Sache		if (rval >= 0) {
140089622Sache			opieunlock();
140117435Spst			goto skip;
140289622Sache		}
140389622Sache#endif
140488763Sache		if (opieverify(&opiedata, passwd) == 0)
140588763Sache			xpasswd = pw->pw_passwd;
140689622Sache		else if (pwok) {
140788763Sache			xpasswd = crypt(passwd, pw->pw_passwd);
140889622Sache			if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0')
140989622Sache				xpasswd = ":";
141089622Sache		} else {
141188763Sache			rval = 1;
141288763Sache			goto skip;
141388763Sache		}
141488763Sache		rval = strcmp(pw->pw_passwd, xpasswd);
141589622Sache		if (pw->pw_expire && time(NULL) >= pw->pw_expire)
141617435Spst			rval = 1;	/* failure */
141717435Spstskip:
141817435Spst		/*
141917435Spst		 * If rval == 1, the user failed the authentication check
142051433Smarkm		 * above.  If rval == 0, either PAM or local authentication
142117435Spst		 * succeeded.
142217435Spst		 */
142317435Spst		if (rval) {
14241592Srgrimes			reply(530, "Login incorrect.");
1425301241Slidl#ifdef USE_BLACKLIST
1426301517Slidl			blacklist_notify(1, STDIN_FILENO, "Login incorrect");
1427301241Slidl#endif
1428110691Syar			if (logging) {
14291592Srgrimes				syslog(LOG_NOTICE,
1430110691Syar				    "FTP LOGIN FAILED FROM %s",
1431110691Syar				    remotehost);
1432110691Syar				syslog(LOG_AUTHPRIV | LOG_NOTICE,
14331592Srgrimes				    "FTP LOGIN FAILED FROM %s, %s",
14341592Srgrimes				    remotehost, curname);
1435110691Syar			}
14361592Srgrimes			pw = NULL;
14371592Srgrimes			if (login_attempts++ >= 5) {
14381592Srgrimes				syslog(LOG_NOTICE,
14391592Srgrimes				    "repeated login failures from %s",
14401592Srgrimes				    remotehost);
14411592Srgrimes				exit(0);
14421592Srgrimes			}
14431592Srgrimes			return;
14441592Srgrimes		}
1445301241Slidl#ifdef USE_BLACKLIST
1446301241Slidl		 else {
1447301517Slidl			blacklist_notify(0, STDIN_FILENO, "Login successful");
1448301241Slidl		}
1449301241Slidl#endif
14501592Srgrimes	}
14511592Srgrimes	login_attempts = 0;		/* this time successful */
1452132894Syar	if (setegid(pw->pw_gid) < 0) {
14531592Srgrimes		reply(550, "Can't set gid.");
14541592Srgrimes		return;
14551592Srgrimes	}
145625101Sdavidn	/* May be overridden by login.conf */
145725101Sdavidn	(void) umask(defumask);
145825101Sdavidn#ifdef	LOGIN_CAP
145925674Sdavidn	if ((lc = login_getpwclass(pw)) != NULL) {
1460137983Syar		char	remote_ip[NI_MAXHOST];
146125101Sdavidn
1462137983Syar		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
146356668Sshin			remote_ip, sizeof(remote_ip) - 1, NULL, 0,
1464137983Syar			NI_NUMERICHOST))
1465137983Syar				*remote_ip = 0;
146625101Sdavidn		remote_ip[sizeof(remote_ip) - 1] = 0;
146725101Sdavidn		if (!auth_hostok(lc, remotehost, remote_ip)) {
146825101Sdavidn			syslog(LOG_INFO|LOG_AUTH,
146925101Sdavidn			    "FTP LOGIN FAILED (HOST) as %s: permission denied.",
147025101Sdavidn			    pw->pw_name);
1471137849Syar			reply(530, "Permission denied.");
147225101Sdavidn			pw = NULL;
147325101Sdavidn			return;
147425101Sdavidn		}
147525101Sdavidn		if (!auth_timeok(lc, time(NULL))) {
1476137849Syar			reply(530, "Login not available right now.");
147725101Sdavidn			pw = NULL;
147825101Sdavidn			return;
147925101Sdavidn		}
148025101Sdavidn	}
1481223434Strasz	setusercontext(lc, pw, 0, LOGIN_SETALL &
1482223434Strasz		       ~(LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV));
148325101Sdavidn#else
148440310Sdes	setlogin(pw->pw_name);
14851592Srgrimes	(void) initgroups(pw->pw_name, pw->pw_gid);
148625101Sdavidn#endif
14871592Srgrimes
148874874Smarkm#ifdef USE_PAM
148974874Smarkm	if (pamh) {
149074874Smarkm		if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
149174874Smarkm			syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
149274874Smarkm		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
149374874Smarkm			syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
149474874Smarkm		}
149574874Smarkm	}
149674874Smarkm#endif
149774874Smarkm
1498202209Sed	dochroot =
1499216932Scsjp		checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode)
1500202209Sed#ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
1501202209Sed		|| login_getcapbool(lc, "ftp-chroot", 0)
1502202209Sed#endif
1503202209Sed	;
1504216932Scsjp	/*
1505216932Scsjp	 * It is possible that checkuser() failed to open the chroot file.
1506216932Scsjp	 * If this is the case, report that logins are un-available, since we
1507216932Scsjp	 * have no way of checking whether or not the user should be chrooted.
1508216932Scsjp	 * We ignore ENOENT since it is not required that this file be present.
1509216932Scsjp	 */
1510216932Scsjp	if (ecode != 0 && ecode != ENOENT) {
1511216932Scsjp		reply(530, "Login not available right now.");
1512216932Scsjp		return;
1513216932Scsjp	}
1514202209Sed	chrootdir = NULL;
1515202209Sed
1516202604Sed	/* Disable wtmp logging when chrooting. */
1517202604Sed	if (dochroot || guest)
1518202604Sed		dowtmp = 0;
1519202604Sed	if (dowtmp)
1520202209Sed		ftpd_logwtmp(wtmpid, pw->pw_name,
1521102311Syar		    (struct sockaddr *)&his_addr);
15221592Srgrimes	logged_in = 1;
15231592Srgrimes
152417435Spst	if (guest && stats && statfd < 0)
152525283Sdavidn#ifdef VIRTUAL_HOSTING
1526130428Sobrien		statfd = open(thishost->statfile, O_WRONLY|O_APPEND);
152725283Sdavidn#else
1528130428Sobrien		statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND);
152925283Sdavidn#endif
1530130428Sobrien		if (statfd < 0)
15316740Sguido			stats = 0;
15326740Sguido
1533110036Syar	/*
1534110036Syar	 * For a chrooted local user,
1535110036Syar	 * a) see whether ftpchroot(5) specifies a chroot directory,
1536110036Syar	 * b) extract the directory pathname from the line,
1537110036Syar	 * c) expand it to the absolute pathname if necessary.
1538110036Syar	 */
1539110036Syar	if (dochroot && residue &&
1540117349Syar	    (chrootdir = strtok(residue, " \t")) != NULL) {
1541117349Syar		if (chrootdir[0] != '/')
1542117349Syar			asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir);
1543117349Syar		else
1544137862Syar			chrootdir = strdup(chrootdir); /* make it permanent */
1545110036Syar		if (chrootdir == NULL)
1546110036Syar			fatalerror("Ran out of memory.");
1547110036Syar	}
1548110036Syar	if (guest || dochroot) {
15491592Srgrimes		/*
1550110036Syar		 * If no chroot directory set yet, use the login directory.
1551110036Syar		 * Copy it so it can be modified while pw->pw_dir stays intact.
15521592Srgrimes		 */
1553110036Syar		if (chrootdir == NULL &&
1554110036Syar		    (chrootdir = strdup(pw->pw_dir)) == NULL)
1555110036Syar			fatalerror("Ran out of memory.");
1556110036Syar		/*
1557110036Syar		 * Check for the "/chroot/./home" syntax,
1558110036Syar		 * separate the chroot and home directory pathnames.
1559110036Syar		 */
1560110036Syar		if ((homedir = strstr(chrootdir, "/./")) != NULL) {
1561110036Syar			*(homedir++) = '\0';	/* wipe '/' */
1562110036Syar			homedir++;		/* skip '.' */
1563110036Syar		} else {
1564110036Syar			/*
1565110036Syar			 * We MUST do a chdir() after the chroot. Otherwise
1566110036Syar			 * the old current directory will be accessible as "."
1567110036Syar			 * outside the new root!
1568110036Syar			 */
1569110036Syar			homedir = "/";
1570109939Syar		}
1571110036Syar		/*
1572110036Syar		 * Finally, do chroot()
1573110036Syar		 */
1574110036Syar		if (chroot(chrootdir) < 0) {
157517435Spst			reply(550, "Can't change root.");
157617435Spst			goto bad;
157717435Spst		}
1578228843Scperciva		__FreeBSD_libc_enter_restricted_mode();
1579110036Syar	} else	/* real user w/o chroot */
1580110036Syar		homedir = pw->pw_dir;
1581110036Syar	/*
1582110036Syar	 * Set euid *before* doing chdir() so
1583110036Syar	 * a) the user won't be carried to a directory that he couldn't reach
1584110036Syar	 *    on his own due to no permission to upper path components,
1585110036Syar	 * b) NFS mounted homedirs w/restrictive permissions will be accessible
1586110036Syar	 *    (uid 0 has no root power over NFS if not mapped explicitly.)
1587110036Syar	 */
1588132893Syar	if (seteuid(pw->pw_uid) < 0) {
15891592Srgrimes		reply(550, "Can't set uid.");
15901592Srgrimes		goto bad;
15911592Srgrimes	}
1592110036Syar	if (chdir(homedir) < 0) {
1593110036Syar		if (guest || dochroot) {
1594110036Syar			reply(550, "Can't change to base directory.");
1595110036Syar			goto bad;
1596110036Syar		} else {
1597110036Syar			if (chdir("/") < 0) {
1598110036Syar				reply(550, "Root is inaccessible.");
1599110036Syar				goto bad;
1600110036Syar			}
1601137852Syar			lreply(230, "No directory! Logging in with home=/.");
1602110036Syar		}
1603110036Syar	}
16048696Sdg
16051592Srgrimes	/*
16061592Srgrimes	 * Display a login message, if it exists.
16071592Srgrimes	 * N.B. reply(230,) must follow the message.
16081592Srgrimes	 */
160925283Sdavidn#ifdef VIRTUAL_HOSTING
1610130428Sobrien	fd = fopen(thishost->loginmsg, "r");
161125283Sdavidn#else
1612130428Sobrien	fd = fopen(_PATH_FTPLOGINMESG, "r");
161325283Sdavidn#endif
1614130428Sobrien	if (fd != NULL) {
16151592Srgrimes		char *cp, line[LINE_MAX];
16161592Srgrimes
16171592Srgrimes		while (fgets(line, sizeof(line), fd) != NULL) {
16181592Srgrimes			if ((cp = strchr(line, '\n')) != NULL)
16191592Srgrimes				*cp = '\0';
16201592Srgrimes			lreply(230, "%s", line);
16211592Srgrimes		}
16221592Srgrimes		(void) fflush(stdout);
16231592Srgrimes		(void) fclose(fd);
16241592Srgrimes	}
16251592Srgrimes	if (guest) {
16266740Sguido		if (ident != NULL)
16276740Sguido			free(ident);
162817433Spst		ident = strdup(passwd);
162917433Spst		if (ident == NULL)
163076096Smarkm			fatalerror("Ran out of memory.");
163117433Spst
16321592Srgrimes		reply(230, "Guest login ok, access restrictions apply.");
16331592Srgrimes#ifdef SETPROCTITLE
163425283Sdavidn#ifdef VIRTUAL_HOSTING
163525283Sdavidn		if (thishost != firsthost)
163625283Sdavidn			snprintf(proctitle, sizeof(proctitle),
163783308Smikeh				 "%s: anonymous(%s)/%s", remotehost, hostname,
163883308Smikeh				 passwd);
163925283Sdavidn		else
164025283Sdavidn#endif
164125283Sdavidn			snprintf(proctitle, sizeof(proctitle),
164283308Smikeh				 "%s: anonymous/%s", remotehost, passwd);
164313139Speter		setproctitle("%s", proctitle);
16441592Srgrimes#endif /* SETPROCTITLE */
16451592Srgrimes		if (logging)
16461592Srgrimes			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
16471592Srgrimes			    remotehost, passwd);
16481592Srgrimes	} else {
164984841Syar		if (dochroot)
165084841Syar			reply(230, "User %s logged in, "
165184841Syar				   "access restrictions apply.", pw->pw_name);
165284841Syar		else
165384841Syar			reply(230, "User %s logged in.", pw->pw_name);
165425986Sdanny
16551592Srgrimes#ifdef SETPROCTITLE
16561592Srgrimes		snprintf(proctitle, sizeof(proctitle),
165784842Syar			 "%s: user/%s", remotehost, pw->pw_name);
165813139Speter		setproctitle("%s", proctitle);
16591592Srgrimes#endif /* SETPROCTITLE */
16601592Srgrimes		if (logging)
16611592Srgrimes			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
16621592Srgrimes			    remotehost, pw->pw_name);
16631592Srgrimes	}
1664140473Syar	if (logging && (guest || dochroot))
1665137985Syar		syslog(LOG_INFO, "session root changed to %s", chrootdir);
166625101Sdavidn#ifdef	LOGIN_CAP
166725101Sdavidn	login_close(lc);
166825101Sdavidn#endif
1669110036Syar	if (residue)
1670110036Syar		free(residue);
16711592Srgrimes	return;
16721592Srgrimesbad:
16731592Srgrimes	/* Forget all about it... */
167425101Sdavidn#ifdef	LOGIN_CAP
167525101Sdavidn	login_close(lc);
167625101Sdavidn#endif
1677110036Syar	if (residue)
1678110036Syar		free(residue);
16791592Srgrimes	end_login();
16801592Srgrimes}
16811592Srgrimes
16821592Srgrimesvoid
168390148Simpretrieve(char *cmd, char *name)
16841592Srgrimes{
16851592Srgrimes	FILE *fin, *dout;
16861592Srgrimes	struct stat st;
168790148Simp	int (*closefunc)(FILE *);
168836612Sjb	time_t start;
1689299585Struckman	char line[BUFSIZ];
16901592Srgrimes
16911592Srgrimes	if (cmd == 0) {
16921592Srgrimes		fin = fopen(name, "r"), closefunc = fclose;
16931592Srgrimes		st.st_size = 0;
16941592Srgrimes	} else {
1695299585Struckman		(void) snprintf(line, sizeof(line), cmd, name);
1696299585Struckman		name = line;
16971592Srgrimes		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
16981592Srgrimes		st.st_size = -1;
16991592Srgrimes		st.st_blksize = BUFSIZ;
17001592Srgrimes	}
17011592Srgrimes	if (fin == NULL) {
17021592Srgrimes		if (errno != 0) {
17031592Srgrimes			perror_reply(550, name);
17041592Srgrimes			if (cmd == 0) {
17051592Srgrimes				LOGCMD("get", name);
17061592Srgrimes			}
17071592Srgrimes		}
17081592Srgrimes		return;
17091592Srgrimes	}
17101592Srgrimes	byte_count = -1;
1711110144Syar	if (cmd == 0) {
1712110144Syar		if (fstat(fileno(fin), &st) < 0) {
1713110144Syar			perror_reply(550, name);
1714110144Syar			goto done;
1715110144Syar		}
1716110144Syar		if (!S_ISREG(st.st_mode)) {
1717125565Syar			/*
1718125565Syar			 * Never sending a raw directory is a workaround
1719125565Syar			 * for buggy clients that will attempt to RETR
1720125565Syar			 * a directory before listing it, e.g., Mozilla.
1721125565Syar			 * Preventing a guest from getting irregular files
1722125565Syar			 * is a simple security measure.
1723125565Syar			 */
1724125565Syar			if (S_ISDIR(st.st_mode) || guest) {
1725110144Syar				reply(550, "%s: not a plain file.", name);
1726110144Syar				goto done;
1727110144Syar			}
1728110144Syar			st.st_size = -1;
1729110144Syar			/* st.st_blksize is set for all descriptor types */
1730110144Syar		}
17311592Srgrimes	}
17321592Srgrimes	if (restart_point) {
17331592Srgrimes		if (type == TYPE_A) {
17341592Srgrimes			off_t i, n;
17351592Srgrimes			int c;
17361592Srgrimes
17371592Srgrimes			n = restart_point;
17381592Srgrimes			i = 0;
17391592Srgrimes			while (i++ < n) {
17401592Srgrimes				if ((c=getc(fin)) == EOF) {
17411592Srgrimes					perror_reply(550, name);
17421592Srgrimes					goto done;
17431592Srgrimes				}
17441592Srgrimes				if (c == '\n')
17451592Srgrimes					i++;
17461592Srgrimes			}
17471592Srgrimes		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
17481592Srgrimes			perror_reply(550, name);
17491592Srgrimes			goto done;
17501592Srgrimes		}
17511592Srgrimes	}
17521592Srgrimes	dout = dataconn(name, st.st_size, "w");
17531592Srgrimes	if (dout == NULL)
17541592Srgrimes		goto done;
17556740Sguido	time(&start);
17568240Swollman	send_data(fin, dout, st.st_blksize, st.st_size,
17578240Swollman		  restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
1758136929Syar	if (cmd == 0 && guest && stats && byte_count > 0)
1759136929Syar		logxfer(name, byte_count, start);
17601592Srgrimes	(void) fclose(dout);
17611592Srgrimes	data = -1;
17621592Srgrimes	pdata = -1;
17631592Srgrimesdone:
17641592Srgrimes	if (cmd == 0)
17651592Srgrimes		LOGBYTES("get", name, byte_count);
17661592Srgrimes	(*closefunc)(fin);
17671592Srgrimes}
17681592Srgrimes
17691592Srgrimesvoid
177090148Simpstore(char *name, char *mode, int unique)
17711592Srgrimes{
1772101537Syar	int fd;
17731592Srgrimes	FILE *fout, *din;
177490148Simp	int (*closefunc)(FILE *);
17751592Srgrimes
1776101537Syar	if (*mode == 'a') {		/* APPE */
1777101537Syar		if (unique) {
1778101537Syar			/* Programming error */
1779101537Syar			syslog(LOG_ERR, "Internal: unique flag to APPE");
1780101537Syar			unique = 0;
1781101537Syar		}
1782101537Syar		if (guest && noguestmod) {
1783137852Syar			reply(550, "Appending to existing file denied.");
1784101537Syar			goto err;
1785101537Syar		}
1786101537Syar		restart_point = 0;	/* not affected by preceding REST */
17871592Srgrimes	}
1788101537Syar	if (unique)			/* STOU overrides REST */
1789101537Syar		restart_point = 0;
1790101537Syar	if (guest && noguestmod) {
1791101537Syar		if (restart_point) {	/* guest STOR w/REST */
1792137852Syar			reply(550, "Modifying existing file denied.");
1793101537Syar			goto err;
1794101537Syar		} else			/* treat guest STOR as STOU */
1795101537Syar			unique = 1;
1796101537Syar	}
17971592Srgrimes
17981592Srgrimes	if (restart_point)
1799101537Syar		mode = "r+";	/* so ASCII manual seek can work */
1800101537Syar	if (unique) {
1801101537Syar		if ((fd = guniquefd(name, &name)) < 0)
1802101537Syar			goto err;
1803101537Syar		fout = fdopen(fd, mode);
1804101537Syar	} else
1805101537Syar		fout = fopen(name, mode);
18061592Srgrimes	closefunc = fclose;
18071592Srgrimes	if (fout == NULL) {
18081592Srgrimes		perror_reply(553, name);
1809101537Syar		goto err;
18101592Srgrimes	}
18111592Srgrimes	byte_count = -1;
18121592Srgrimes	if (restart_point) {
18131592Srgrimes		if (type == TYPE_A) {
18141592Srgrimes			off_t i, n;
18151592Srgrimes			int c;
18161592Srgrimes
18171592Srgrimes			n = restart_point;
18181592Srgrimes			i = 0;
18191592Srgrimes			while (i++ < n) {
18201592Srgrimes				if ((c=getc(fout)) == EOF) {
18211592Srgrimes					perror_reply(550, name);
18221592Srgrimes					goto done;
18231592Srgrimes				}
18241592Srgrimes				if (c == '\n')
18251592Srgrimes					i++;
18261592Srgrimes			}
18271592Srgrimes			/*
18281592Srgrimes			 * We must do this seek to "current" position
18291592Srgrimes			 * because we are changing from reading to
18301592Srgrimes			 * writing.
18311592Srgrimes			 */
1832132930Syar			if (fseeko(fout, 0, SEEK_CUR) < 0) {
18331592Srgrimes				perror_reply(550, name);
18341592Srgrimes				goto done;
18351592Srgrimes			}
18361592Srgrimes		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
18371592Srgrimes			perror_reply(550, name);
18381592Srgrimes			goto done;
18391592Srgrimes		}
18401592Srgrimes	}
1841132930Syar	din = dataconn(name, -1, "r");
18421592Srgrimes	if (din == NULL)
18431592Srgrimes		goto done;
18441592Srgrimes	if (receive_data(din, fout) == 0) {
18451592Srgrimes		if (unique)
18461592Srgrimes			reply(226, "Transfer complete (unique file name:%s).",
18471592Srgrimes			    name);
18481592Srgrimes		else
18491592Srgrimes			reply(226, "Transfer complete.");
18501592Srgrimes	}
18511592Srgrimes	(void) fclose(din);
18521592Srgrimes	data = -1;
18531592Srgrimes	pdata = -1;
18541592Srgrimesdone:
1855102566Syar	LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count);
18561592Srgrimes	(*closefunc)(fout);
1857101537Syar	return;
1858101537Syarerr:
1859101537Syar	LOGCMD(*mode == 'a' ? "append" : "put" , name);
1860101537Syar	return;
18611592Srgrimes}
18621592Srgrimes
18631592Srgrimesstatic FILE *
186490148Simpgetdatasock(char *mode)
18651592Srgrimes{
18661592Srgrimes	int on = 1, s, t, tries;
18671592Srgrimes
18681592Srgrimes	if (data >= 0)
18691592Srgrimes		return (fdopen(data, mode));
187056668Sshin
187156668Sshin	s = socket(data_dest.su_family, SOCK_STREAM, 0);
18721592Srgrimes	if (s < 0)
18731592Srgrimes		goto bad;
1874100612Syar	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
1875100609Syar		syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m");
18761592Srgrimes	/* anchor socket to avoid multi-homing problems */
187756668Sshin	data_source = ctrl_addr;
1878109742Syar	data_source.su_port = htons(dataport);
1879132893Syar	(void) seteuid(0);
18801592Srgrimes	for (tries = 1; ; tries++) {
1881132891Syar		/*
1882132891Syar		 * We should loop here since it's possible that
1883132891Syar		 * another ftpd instance has passed this point and is
1884132891Syar		 * trying to open a data connection in active mode now.
1885132891Syar		 * Until the other connection is opened, we'll be getting
1886132891Syar		 * EADDRINUSE because no SOCK_STREAM sockets in the system
1887132891Syar		 * can share both local and remote addresses, localIP:20
1888132891Syar		 * and *:* in this case.
1889132891Syar		 */
18901592Srgrimes		if (bind(s, (struct sockaddr *)&data_source,
189156668Sshin		    data_source.su_len) >= 0)
18921592Srgrimes			break;
18931592Srgrimes		if (errno != EADDRINUSE || tries > 10)
18941592Srgrimes			goto bad;
18951592Srgrimes		sleep(tries);
18961592Srgrimes	}
1897132893Syar	(void) seteuid(pw->pw_uid);
18981592Srgrimes#ifdef IP_TOS
189956668Sshin	if (data_source.su_family == AF_INET)
190056668Sshin      {
19011592Srgrimes	on = IPTOS_THROUGHPUT;
1902100612Syar	if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0)
1903100609Syar		syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m");
190456668Sshin      }
19051592Srgrimes#endif
19068240Swollman#ifdef TCP_NOPUSH
19078240Swollman	/*
19088240Swollman	 * Turn off push flag to keep sender TCP from sending short packets
1909166598Syar	 * at the boundaries of each write().
19108240Swollman	 */
19118240Swollman	on = 1;
1912100612Syar	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0)
1913100609Syar		syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m");
19148240Swollman#endif
19151592Srgrimes	return (fdopen(s, mode));
19161592Srgrimesbad:
19171592Srgrimes	/* Return the real value of errno (close may change it) */
19181592Srgrimes	t = errno;
1919132893Syar	(void) seteuid(pw->pw_uid);
19201592Srgrimes	(void) close(s);
19211592Srgrimes	errno = t;
19221592Srgrimes	return (NULL);
19231592Srgrimes}
19241592Srgrimes
19251592Srgrimesstatic FILE *
192690148Simpdataconn(char *name, off_t size, char *mode)
19271592Srgrimes{
19281592Srgrimes	char sizebuf[32];
19291592Srgrimes	FILE *file;
1930109611Scjc	int retry = 0, tos, conerrno;
19311592Srgrimes
19321592Srgrimes	file_size = size;
19331592Srgrimes	byte_count = 0;
1934132930Syar	if (size != -1)
1935132929Syar		(void) snprintf(sizebuf, sizeof(sizebuf),
1936132929Syar				" (%jd bytes)", (intmax_t)size);
19371592Srgrimes	else
193831973Simp		*sizebuf = '\0';
19391592Srgrimes	if (pdata >= 0) {
194056668Sshin		union sockunion from;
1941141918Sstefanf		socklen_t fromlen = ctrl_addr.su_len;
1942141918Sstefanf		int flags, s;
194312532Sguido		struct timeval timeout;
194412532Sguido		fd_set set;
19451592Srgrimes
194612532Sguido		FD_ZERO(&set);
194712532Sguido		FD_SET(pdata, &set);
194812532Sguido
194912532Sguido		timeout.tv_usec = 0;
195012532Sguido		timeout.tv_sec = 120;
195112532Sguido
195286628Syar		/*
195386628Syar		 * Granted a socket is in the blocking I/O mode,
195486628Syar		 * accept() will block after a successful select()
195586628Syar		 * if the selected connection dies in between.
195686628Syar		 * Therefore set the non-blocking I/O flag here.
195786628Syar		 */
195886628Syar		if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
195986628Syar		    fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1)
196086628Syar			goto pdata_err;
1961132931Syar		if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 ||
196286628Syar		    (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0)
196386628Syar			goto pdata_err;
19641592Srgrimes		(void) close(pdata);
19651592Srgrimes		pdata = s;
196686628Syar		/*
1967101809Syar		 * Unset the inherited non-blocking I/O flag
1968101809Syar		 * on the child socket so stdio can work on it.
196986628Syar		 */
197086628Syar		if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
197186628Syar		    fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1)
197286628Syar			goto pdata_err;
19731592Srgrimes#ifdef IP_TOS
197456668Sshin		if (from.su_family == AF_INET)
197556668Sshin	      {
197617435Spst		tos = IPTOS_THROUGHPUT;
1977100612Syar		if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
1978100609Syar			syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m");
197956668Sshin	      }
19801592Srgrimes#endif
19811592Srgrimes		reply(150, "Opening %s mode data connection for '%s'%s.",
19821592Srgrimes		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
19831592Srgrimes		return (fdopen(pdata, mode));
198486628Syarpdata_err:
198586628Syar		reply(425, "Can't open data connection.");
198686628Syar		(void) close(pdata);
198786628Syar		pdata = -1;
198886628Syar		return (NULL);
19891592Srgrimes	}
19901592Srgrimes	if (data >= 0) {
19911592Srgrimes		reply(125, "Using existing data connection for '%s'%s.",
19921592Srgrimes		    name, sizebuf);
19931592Srgrimes		usedefault = 1;
19941592Srgrimes		return (fdopen(data, mode));
19951592Srgrimes	}
19961592Srgrimes	if (usedefault)
19971592Srgrimes		data_dest = his_addr;
19981592Srgrimes	usedefault = 1;
1999109611Scjc	do {
2000109611Scjc		file = getdatasock(mode);
2001109611Scjc		if (file == NULL) {
2002137983Syar			char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
2003137983Syar
2004137983Syar			if (getnameinfo((struct sockaddr *)&data_source,
2005137983Syar				data_source.su_len,
2006137983Syar				hostbuf, sizeof(hostbuf) - 1,
2007137983Syar				portbuf, sizeof(portbuf) - 1,
2008137983Syar				NI_NUMERICHOST|NI_NUMERICSERV))
2009137983Syar					*hostbuf = *portbuf = 0;
2010137983Syar			hostbuf[sizeof(hostbuf) - 1] = 0;
2011137983Syar			portbuf[sizeof(portbuf) - 1] = 0;
2012109611Scjc			reply(425, "Can't create data socket (%s,%s): %s.",
2013109611Scjc				hostbuf, portbuf, strerror(errno));
2014109611Scjc			return (NULL);
2015109611Scjc		}
2016109611Scjc		data = fileno(file);
2017109611Scjc		conerrno = 0;
2018109611Scjc		if (connect(data, (struct sockaddr *)&data_dest,
2019109611Scjc		    data_dest.su_len) == 0)
2020109611Scjc			break;
2021109611Scjc		conerrno = errno;
2022109611Scjc		(void) fclose(file);
2023109611Scjc		data = -1;
2024109611Scjc		if (conerrno == EADDRINUSE) {
2025137659Syar			sleep(swaitint);
20261592Srgrimes			retry += swaitint;
2027109611Scjc		} else {
2028109611Scjc			break;
20291592Srgrimes		}
2030109611Scjc	} while (retry <= swaitmax);
2031109611Scjc	if (conerrno != 0) {
2032137850Syar		reply(425, "Can't build data connection: %s.",
2033137850Syar			   strerror(conerrno));
20341592Srgrimes		return (NULL);
20351592Srgrimes	}
20361592Srgrimes	reply(150, "Opening %s mode data connection for '%s'%s.",
20371592Srgrimes	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
20381592Srgrimes	return (file);
20391592Srgrimes}
20401592Srgrimes
20411592Srgrimes/*
2042140472Syar * A helper macro to avoid code duplication
2043140472Syar * in send_data() and receive_data().
2044140472Syar *
2045140472Syar * XXX We have to block SIGURG during putc() because BSD stdio
2046140472Syar * is unable to restart interrupted write operations and hence
2047140472Syar * the entire buffer contents will be lost as soon as a write()
2048140472Syar * call indicates EINTR to stdio.
2049140472Syar */
2050140472Syar#define FTPD_PUTC(ch, file, label)					\
2051140472Syar	do {								\
2052140472Syar		int ret;						\
2053140472Syar									\
2054140472Syar		do {							\
2055140472Syar			START_UNSAFE;					\
2056140472Syar			ret = putc((ch), (file));			\
2057140472Syar			END_UNSAFE;					\
2058140472Syar			CHECKOOB(return (-1))				\
2059140472Syar			else if (ferror(file))				\
2060140472Syar				goto label;				\
2061140472Syar			clearerr(file);					\
2062140472Syar		} while (ret == EOF);					\
2063140472Syar	} while (0)
2064140472Syar
2065140472Syar/*
2066298897Spfg * Transfer the contents of "instr" to "outstr" peer using the appropriate
20678240Swollman * encapsulation of the data subject to Mode, Structure, and Type.
20681592Srgrimes *
20691592Srgrimes * NB: Form isn't handled.
20701592Srgrimes */
207189935Syarstatic int
2072137660Syarsend_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg)
20731592Srgrimes{
2074122751Syar	int c, cp, filefd, netfd;
207570205Sdan	char *buf;
20761592Srgrimes
2077140472Syar	STARTXFER;
2078140472Syar
20791592Srgrimes	switch (type) {
20801592Srgrimes
20811592Srgrimes	case TYPE_A:
2082140472Syar		cp = EOF;
2083140472Syar		for (;;) {
2084140472Syar			c = getc(instr);
2085140472Syar			CHECKOOB(return (-1))
2086140472Syar			else if (c == EOF && ferror(instr))
2087140472Syar				goto file_err;
2088140472Syar			if (c == EOF) {
2089140472Syar				if (ferror(instr)) {	/* resume after OOB */
2090140472Syar					clearerr(instr);
2091140472Syar					continue;
2092140472Syar				}
2093140472Syar				if (feof(instr))	/* EOF */
2094140472Syar					break;
2095140472Syar				syslog(LOG_ERR, "Internal: impossible condition"
2096140472Syar						" on file after getc()");
2097140472Syar				goto file_err;
2098140472Syar			}
2099122751Syar			if (c == '\n' && cp != '\r') {
2100140472Syar				FTPD_PUTC('\r', outstr, data_err);
2101140472Syar				byte_count++;
21021592Srgrimes			}
2103140472Syar			FTPD_PUTC(c, outstr, data_err);
2104140472Syar			byte_count++;
2105122751Syar			cp = c;
21061592Srgrimes		}
2107140472Syar#ifdef notyet	/* BSD stdio isn't ready for that */
2108140472Syar		while (fflush(outstr) == EOF) {
2109140472Syar			CHECKOOB(return (-1))
2110140472Syar			else
2111140472Syar				goto data_err;
2112140472Syar			clearerr(outstr);
2113140472Syar		}
2114140472Syar		ENDXFER;
2115140472Syar#else
2116140472Syar		ENDXFER;
2117140472Syar		if (fflush(outstr) == EOF)
21181592Srgrimes			goto data_err;
2119140472Syar#endif
21201592Srgrimes		reply(226, "Transfer complete.");
212189935Syar		return (0);
21221592Srgrimes
21231592Srgrimes	case TYPE_I:
21241592Srgrimes	case TYPE_L:
21258240Swollman		/*
21268240Swollman		 * isreg is only set if we are not doing restart and we
21278240Swollman		 * are sending a regular file
21288240Swollman		 */
21298240Swollman		netfd = fileno(outstr);
21308870Srgrimes		filefd = fileno(instr);
21318240Swollman
213270205Sdan		if (isreg) {
2133136554Syar			char *msg = "Transfer complete.";
2134140472Syar			off_t cnt, offset;
213570205Sdan			int err;
213670205Sdan
2137136555Syar			cnt = offset = 0;
213870205Sdan
2139136555Syar			while (filesize > 0) {
214090604Smaxim				err = sendfile(filefd, netfd, offset, 0,
2141137811Syar					       NULL, &cnt, 0);
214299212Smaxim				/*
214399212Smaxim				 * Calculate byte_count before OOB processing.
214499212Smaxim				 * It can be used in myoob() later.
214599212Smaxim				 */
214699212Smaxim				byte_count += cnt;
214770205Sdan				offset += cnt;
214890604Smaxim				filesize -= cnt;
2149140472Syar				CHECKOOB(return (-1))
2150140472Syar				else if (err == -1) {
2151140472Syar					if (errno != EINTR &&
2152140472Syar					    cnt == 0 && offset == 0)
215370205Sdan						goto oldway;
215470205Sdan					goto data_err;
215570205Sdan				}
2156140472Syar				if (err == -1)	/* resume after OOB */
2157140472Syar					continue;
2158136554Syar				/*
2159136554Syar				 * We hit the EOF prematurely.
2160136554Syar				 * Perhaps the file was externally truncated.
2161136554Syar				 */
2162136554Syar				if (cnt == 0) {
2163136554Syar					msg = "Transfer finished due to "
2164136554Syar					      "premature end of file.";
2165136554Syar					break;
2166136554Syar				}
216770205Sdan			}
2168140472Syar			ENDXFER;
2169216945Semaste			reply(226, "%s", msg);
217089935Syar			return (0);
21718240Swollman		}
21728240Swollman
21738240Swollmanoldway:
2174137659Syar		if ((buf = malloc(blksize)) == NULL) {
2175140472Syar			ENDXFER;
2176137852Syar			reply(451, "Ran out of memory.");
217789935Syar			return (-1);
21781592Srgrimes		}
21798870Srgrimes
2180140472Syar		for (;;) {
2181140472Syar			int cnt, len;
2182140472Syar			char *bp;
2183140472Syar
2184140472Syar			cnt = read(filefd, buf, blksize);
2185140472Syar			CHECKOOB(free(buf); return (-1))
2186140472Syar			else if (cnt < 0) {
2187140472Syar				free(buf);
21881592Srgrimes				goto file_err;
2189140472Syar			}
2190140472Syar			if (cnt < 0)	/* resume after OOB */
2191140472Syar				continue;
2192140472Syar			if (cnt == 0)	/* EOF */
2193140472Syar				break;
2194140472Syar			for (len = cnt, bp = buf; len > 0;) {
2195140472Syar				cnt = write(netfd, bp, len);
2196140472Syar				CHECKOOB(free(buf); return (-1))
2197140472Syar				else if (cnt < 0) {
2198140472Syar					free(buf);
2199140472Syar					goto data_err;
2200140472Syar				}
2201140472Syar				if (cnt <= 0)
2202140472Syar					continue;
2203140472Syar				len -= cnt;
2204140472Syar				bp += cnt;
2205140472Syar				byte_count += cnt;
2206140472Syar			}
22071592Srgrimes		}
2208140472Syar		ENDXFER;
2209140472Syar		free(buf);
22101592Srgrimes		reply(226, "Transfer complete.");
221189935Syar		return (0);
22121592Srgrimes	default:
2213140472Syar		ENDXFER;
2214137852Syar		reply(550, "Unimplemented TYPE %d in send_data.", type);
221589935Syar		return (-1);
22161592Srgrimes	}
22171592Srgrimes
22181592Srgrimesdata_err:
2219140472Syar	ENDXFER;
22201592Srgrimes	perror_reply(426, "Data connection");
222189935Syar	return (-1);
22221592Srgrimes
22231592Srgrimesfile_err:
2224140472Syar	ENDXFER;
22251592Srgrimes	perror_reply(551, "Error on input file");
222689935Syar	return (-1);
22271592Srgrimes}
22281592Srgrimes
22291592Srgrimes/*
22301592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of
22311592Srgrimes * the data subject to Mode, Structure, and Type.
22321592Srgrimes *
22331592Srgrimes * N.B.: Form isn't handled.
22341592Srgrimes */
22351592Srgrimesstatic int
223690148Simpreceive_data(FILE *instr, FILE *outstr)
22371592Srgrimes{
2238140472Syar	int c, cp;
2239140472Syar	int bare_lfs = 0;
22401592Srgrimes
2241140472Syar	STARTXFER;
224217433Spst
22431592Srgrimes	switch (type) {
22441592Srgrimes
22451592Srgrimes	case TYPE_I:
22461592Srgrimes	case TYPE_L:
2247140472Syar		for (;;) {
2248140472Syar			int cnt, len;
2249140472Syar			char *bp;
2250140472Syar			char buf[BUFSIZ];
2251140472Syar
2252140472Syar			cnt = read(fileno(instr), buf, sizeof(buf));
2253140472Syar			CHECKOOB(return (-1))
2254140472Syar			else if (cnt < 0)
2255140472Syar				goto data_err;
2256140472Syar			if (cnt < 0)	/* resume after OOB */
2257140472Syar				continue;
2258140472Syar			if (cnt == 0)	/* EOF */
2259140472Syar				break;
2260140472Syar			for (len = cnt, bp = buf; len > 0;) {
2261140472Syar				cnt = write(fileno(outstr), bp, len);
2262140472Syar				CHECKOOB(return (-1))
2263140472Syar				else if (cnt < 0)
2264140472Syar					goto file_err;
2265140472Syar				if (cnt <= 0)
2266140472Syar					continue;
2267140472Syar				len -= cnt;
2268140472Syar				bp += cnt;
2269140472Syar				byte_count += cnt;
2270140472Syar			}
22711592Srgrimes		}
2272140472Syar		ENDXFER;
22731592Srgrimes		return (0);
22741592Srgrimes
22751592Srgrimes	case TYPE_E:
2276140472Syar		ENDXFER;
22771592Srgrimes		reply(553, "TYPE E not implemented.");
22781592Srgrimes		return (-1);
22791592Srgrimes
22801592Srgrimes	case TYPE_A:
2281140472Syar		cp = EOF;
2282140472Syar		for (;;) {
2283140472Syar			c = getc(instr);
2284140472Syar			CHECKOOB(return (-1))
2285140472Syar			else if (c == EOF && ferror(instr))
2286140472Syar				goto data_err;
2287140472Syar			if (c == EOF && ferror(instr)) { /* resume after OOB */
2288140472Syar				clearerr(instr);
2289140472Syar				continue;
2290140472Syar			}
2291140472Syar
2292140472Syar			if (cp == '\r') {
2293140472Syar				if (c != '\n')
2294140472Syar					FTPD_PUTC('\r', outstr, file_err);
2295140472Syar			} else
2296140472Syar				if (c == '\n')
2297140472Syar					bare_lfs++;
2298140472Syar			if (c == '\r') {
2299140472Syar				byte_count++;
2300140472Syar				cp = c;
2301140472Syar				continue;
2302140472Syar			}
2303140472Syar
2304140472Syar			/* Check for EOF here in order not to lose last \r. */
2305140472Syar			if (c == EOF) {
2306140472Syar				if (feof(instr))	/* EOF */
2307140472Syar					break;
2308140472Syar				syslog(LOG_ERR, "Internal: impossible condition"
2309140472Syar						" on data stream after getc()");
2310140472Syar				goto data_err;
2311140472Syar			}
2312140472Syar
23131592Srgrimes			byte_count++;
2314140472Syar			FTPD_PUTC(c, outstr, file_err);
2315140472Syar			cp = c;
23161592Srgrimes		}
2317140472Syar#ifdef notyet	/* BSD stdio isn't ready for that */
2318140472Syar		while (fflush(outstr) == EOF) {
2319140472Syar			CHECKOOB(return (-1))
2320140472Syar			else
2321140472Syar				goto file_err;
2322140472Syar			clearerr(outstr);
2323140472Syar		}
2324140472Syar		ENDXFER;
2325140472Syar#else
2326140472Syar		ENDXFER;
2327140472Syar		if (fflush(outstr) == EOF)
23281592Srgrimes			goto file_err;
2329140472Syar#endif
23301592Srgrimes		if (bare_lfs) {
23311592Srgrimes			lreply(226,
2332137852Syar		"WARNING! %d bare linefeeds received in ASCII mode.",
23331592Srgrimes			    bare_lfs);
23341592Srgrimes		(void)printf("   File may not have transferred correctly.\r\n");
23351592Srgrimes		}
23361592Srgrimes		return (0);
23371592Srgrimes	default:
2338140472Syar		ENDXFER;
2339137852Syar		reply(550, "Unimplemented TYPE %d in receive_data.", type);
23401592Srgrimes		return (-1);
23411592Srgrimes	}
23421592Srgrimes
23431592Srgrimesdata_err:
2344140472Syar	ENDXFER;
2345137852Syar	perror_reply(426, "Data connection");
23461592Srgrimes	return (-1);
23471592Srgrimes
23481592Srgrimesfile_err:
2349140472Syar	ENDXFER;
2350137852Syar	perror_reply(452, "Error writing to file");
23511592Srgrimes	return (-1);
23521592Srgrimes}
23531592Srgrimes
23541592Srgrimesvoid
235590148Simpstatfilecmd(char *filename)
23561592Srgrimes{
23571592Srgrimes	FILE *fin;
2358109382Syar	int atstart;
2359137728Syar	int c, code;
23601592Srgrimes	char line[LINE_MAX];
2361137728Syar	struct stat st;
23621592Srgrimes
2363137728Syar	code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213;
236425165Sdavidn	(void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename);
23651592Srgrimes	fin = ftpd_popen(line, "r");
2366216943Semaste	if (fin == NULL) {
2367216943Semaste		perror_reply(551, filename);
2368216943Semaste		return;
2369216943Semaste	}
2370137729Syar	lreply(code, "Status of %s:", filename);
2371109382Syar	atstart = 1;
23721592Srgrimes	while ((c = getc(fin)) != EOF) {
23731592Srgrimes		if (c == '\n') {
23741592Srgrimes			if (ferror(stdout)){
2375137852Syar				perror_reply(421, "Control connection");
23761592Srgrimes				(void) ftpd_pclose(fin);
23771592Srgrimes				dologout(1);
23781592Srgrimes				/* NOTREACHED */
23791592Srgrimes			}
23801592Srgrimes			if (ferror(fin)) {
23811592Srgrimes				perror_reply(551, filename);
23821592Srgrimes				(void) ftpd_pclose(fin);
23831592Srgrimes				return;
23841592Srgrimes			}
23851592Srgrimes			(void) putc('\r', stdout);
23861592Srgrimes		}
2387109382Syar		/*
2388109382Syar		 * RFC 959 says neutral text should be prepended before
2389109382Syar		 * a leading 3-digit number followed by whitespace, but
2390109382Syar		 * many ftp clients can be confused by any leading digits,
2391109382Syar		 * as a matter of fact.
2392109382Syar		 */
2393109382Syar		if (atstart && isdigit(c))
2394109382Syar			(void) putc(' ', stdout);
23951592Srgrimes		(void) putc(c, stdout);
2396109382Syar		atstart = (c == '\n');
23971592Srgrimes	}
23981592Srgrimes	(void) ftpd_pclose(fin);
2399137852Syar	reply(code, "End of status.");
24001592Srgrimes}
24011592Srgrimes
24021592Srgrimesvoid
240390148Simpstatcmd(void)
24041592Srgrimes{
240556668Sshin	union sockunion *su;
24061592Srgrimes	u_char *a, *p;
240799255Sume	char hname[NI_MAXHOST];
240856668Sshin	int ispassive;
24091592Srgrimes
2410110037Syar	if (hostinfo) {
2411110037Syar		lreply(211, "%s FTP server status:", hostname);
2412110037Syar		printf("     %s\r\n", version);
2413110037Syar	} else
2414110037Syar		lreply(211, "FTP server status:");
24151592Srgrimes	printf("     Connected to %s", remotehost);
241656668Sshin	if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
241799255Sume			 hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) {
2418137983Syar		hname[sizeof(hname) - 1] = 0;
241956668Sshin		if (strcmp(hname, remotehost) != 0)
242056668Sshin			printf(" (%s)", hname);
242156668Sshin	}
24221592Srgrimes	printf("\r\n");
24231592Srgrimes	if (logged_in) {
24241592Srgrimes		if (guest)
24251592Srgrimes			printf("     Logged in anonymously\r\n");
24261592Srgrimes		else
24271592Srgrimes			printf("     Logged in as %s\r\n", pw->pw_name);
24281592Srgrimes	} else if (askpasswd)
24291592Srgrimes		printf("     Waiting for password\r\n");
24301592Srgrimes	else
24311592Srgrimes		printf("     Waiting for user name\r\n");
24321592Srgrimes	printf("     TYPE: %s", typenames[type]);
24331592Srgrimes	if (type == TYPE_A || type == TYPE_E)
24341592Srgrimes		printf(", FORM: %s", formnames[form]);
24351592Srgrimes	if (type == TYPE_L)
2436103949Smike#if CHAR_BIT == 8
2437103949Smike		printf(" %d", CHAR_BIT);
24381592Srgrimes#else
24391592Srgrimes		printf(" %d", bytesize);	/* need definition! */
24401592Srgrimes#endif
24411592Srgrimes	printf("; STRUcture: %s; transfer MODE: %s\r\n",
24421592Srgrimes	    strunames[stru], modenames[mode]);
24431592Srgrimes	if (data != -1)
24441592Srgrimes		printf("     Data connection open\r\n");
24451592Srgrimes	else if (pdata != -1) {
244656668Sshin		ispassive = 1;
244756668Sshin		su = &pasv_addr;
24481592Srgrimes		goto printaddr;
24491592Srgrimes	} else if (usedefault == 0) {
245056668Sshin		ispassive = 0;
245156668Sshin		su = &data_dest;
24521592Srgrimesprintaddr:
24531592Srgrimes#define UC(b) (((int) b) & 0xff)
245456668Sshin		if (epsvall) {
245556668Sshin			printf("     EPSV only mode (EPSV ALL)\r\n");
245656668Sshin			goto epsvonly;
245756668Sshin		}
245856668Sshin
245956668Sshin		/* PORT/PASV */
246056668Sshin		if (su->su_family == AF_INET) {
246156668Sshin			a = (u_char *) &su->su_sin.sin_addr;
246256668Sshin			p = (u_char *) &su->su_sin.sin_port;
246356668Sshin			printf("     %s (%d,%d,%d,%d,%d,%d)\r\n",
246456668Sshin				ispassive ? "PASV" : "PORT",
246556668Sshin				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
246656668Sshin				UC(p[0]), UC(p[1]));
246756668Sshin		}
246856668Sshin
246956668Sshin		/* LPRT/LPSV */
247056668Sshin	    {
247156668Sshin		int alen, af, i;
247256668Sshin
247356668Sshin		switch (su->su_family) {
247456668Sshin		case AF_INET:
247556668Sshin			a = (u_char *) &su->su_sin.sin_addr;
247656668Sshin			p = (u_char *) &su->su_sin.sin_port;
247756668Sshin			alen = sizeof(su->su_sin.sin_addr);
247856668Sshin			af = 4;
247956668Sshin			break;
248056668Sshin		case AF_INET6:
248156668Sshin			a = (u_char *) &su->su_sin6.sin6_addr;
248256668Sshin			p = (u_char *) &su->su_sin6.sin6_port;
248356668Sshin			alen = sizeof(su->su_sin6.sin6_addr);
248456668Sshin			af = 6;
248556668Sshin			break;
248656668Sshin		default:
248756668Sshin			af = 0;
248856668Sshin			break;
248956668Sshin		}
249056668Sshin		if (af) {
249156668Sshin			printf("     %s (%d,%d,", ispassive ? "LPSV" : "LPRT",
249256668Sshin				af, alen);
249356668Sshin			for (i = 0; i < alen; i++)
249456668Sshin				printf("%d,", UC(a[i]));
249556668Sshin			printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
249656668Sshin		}
249756668Sshin	    }
249856668Sshin
249956668Sshinepsvonly:;
250056668Sshin		/* EPRT/EPSV */
250156668Sshin	    {
250256668Sshin		int af;
250356668Sshin
250456668Sshin		switch (su->su_family) {
250556668Sshin		case AF_INET:
250656668Sshin			af = 1;
250756668Sshin			break;
250856668Sshin		case AF_INET6:
250956668Sshin			af = 2;
251056668Sshin			break;
251156668Sshin		default:
251256668Sshin			af = 0;
251356668Sshin			break;
251456668Sshin		}
251556668Sshin		if (af) {
251699255Sume			union sockunion tmp;
251799255Sume
251899255Sume			tmp = *su;
251999255Sume			if (tmp.su_family == AF_INET6)
252099255Sume				tmp.su_sin6.sin6_scope_id = 0;
252199255Sume			if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len,
252256668Sshin					hname, sizeof(hname) - 1, NULL, 0,
252356668Sshin					NI_NUMERICHOST)) {
2524137983Syar				hname[sizeof(hname) - 1] = 0;
252556668Sshin				printf("     %s |%d|%s|%d|\r\n",
252656668Sshin					ispassive ? "EPSV" : "EPRT",
252799255Sume					af, hname, htons(tmp.su_port));
252856668Sshin			}
252956668Sshin		}
253056668Sshin	    }
25311592Srgrimes#undef UC
25321592Srgrimes	} else
25331592Srgrimes		printf("     No data connection\r\n");
2534137852Syar	reply(211, "End of status.");
25351592Srgrimes}
25361592Srgrimes
25371592Srgrimesvoid
253890148Simpfatalerror(char *s)
25391592Srgrimes{
25401592Srgrimes
2541137849Syar	reply(451, "Error in server: %s", s);
25421592Srgrimes	reply(221, "Closing connection due to server error.");
25431592Srgrimes	dologout(0);
25441592Srgrimes	/* NOTREACHED */
25451592Srgrimes}
25461592Srgrimes
25471592Srgrimesvoid
25481592Srgrimesreply(int n, const char *fmt, ...)
25491592Srgrimes{
25501592Srgrimes	va_list ap;
255190148Simp
2552129170Stjr	(void)printf("%d ", n);
25531592Srgrimes	va_start(ap, fmt);
25541592Srgrimes	(void)vprintf(fmt, ap);
2555129170Stjr	va_end(ap);
25561592Srgrimes	(void)printf("\r\n");
25571592Srgrimes	(void)fflush(stdout);
255876096Smarkm	if (ftpdebug) {
25591592Srgrimes		syslog(LOG_DEBUG, "<--- %d ", n);
2560129170Stjr		va_start(ap, fmt);
25611592Srgrimes		vsyslog(LOG_DEBUG, fmt, ap);
2562129170Stjr		va_end(ap);
25631592Srgrimes	}
25641592Srgrimes}
25651592Srgrimes
25661592Srgrimesvoid
25671592Srgrimeslreply(int n, const char *fmt, ...)
25681592Srgrimes{
25691592Srgrimes	va_list ap;
257090148Simp
2571129170Stjr	(void)printf("%d- ", n);
25721592Srgrimes	va_start(ap, fmt);
25731592Srgrimes	(void)vprintf(fmt, ap);
2574129170Stjr	va_end(ap);
25751592Srgrimes	(void)printf("\r\n");
25761592Srgrimes	(void)fflush(stdout);
257776096Smarkm	if (ftpdebug) {
25781592Srgrimes		syslog(LOG_DEBUG, "<--- %d- ", n);
2579129170Stjr		va_start(ap, fmt);
25801592Srgrimes		vsyslog(LOG_DEBUG, fmt, ap);
2581129170Stjr		va_end(ap);
25821592Srgrimes	}
25831592Srgrimes}
25841592Srgrimes
25851592Srgrimesstatic void
258690148Simpack(char *s)
25871592Srgrimes{
25881592Srgrimes
25891592Srgrimes	reply(250, "%s command successful.", s);
25901592Srgrimes}
25911592Srgrimes
25921592Srgrimesvoid
259390148Simpnack(char *s)
25941592Srgrimes{
25951592Srgrimes
25961592Srgrimes	reply(502, "%s command not implemented.", s);
25971592Srgrimes}
25981592Srgrimes
25991592Srgrimes/* ARGSUSED */
26001592Srgrimesvoid
260190148Simpyyerror(char *s)
26021592Srgrimes{
26031592Srgrimes	char *cp;
26041592Srgrimes
260517478Smarkm	if ((cp = strchr(cbuf,'\n')))
26061592Srgrimes		*cp = '\0';
2607137852Syar	reply(500, "%s: command not understood.", cbuf);
26081592Srgrimes}
26091592Srgrimes
26101592Srgrimesvoid
261190148Simpdelete(char *name)
26121592Srgrimes{
26131592Srgrimes	struct stat st;
26141592Srgrimes
26151592Srgrimes	LOGCMD("delete", name);
2616100439Syar	if (lstat(name, &st) < 0) {
26171592Srgrimes		perror_reply(550, name);
26181592Srgrimes		return;
26191592Srgrimes	}
2620137847Syar	if (S_ISDIR(st.st_mode)) {
26211592Srgrimes		if (rmdir(name) < 0) {
26221592Srgrimes			perror_reply(550, name);
26231592Srgrimes			return;
26241592Srgrimes		}
26251592Srgrimes		goto done;
26261592Srgrimes	}
2627125568Syar	if (guest && noguestmod) {
2628137852Syar		reply(550, "Operation not permitted.");
2629125568Syar		return;
2630125568Syar	}
2631125568Syar	if (unlink(name) < 0) {
26321592Srgrimes		perror_reply(550, name);
26331592Srgrimes		return;
26341592Srgrimes	}
26351592Srgrimesdone:
26361592Srgrimes	ack("DELE");
26371592Srgrimes}
26381592Srgrimes
26391592Srgrimesvoid
264090148Simpcwd(char *path)
26411592Srgrimes{
26421592Srgrimes
26431592Srgrimes	if (chdir(path) < 0)
26441592Srgrimes		perror_reply(550, path);
26451592Srgrimes	else
26461592Srgrimes		ack("CWD");
26471592Srgrimes}
26481592Srgrimes
26491592Srgrimesvoid
265090148Simpmakedir(char *name)
26511592Srgrimes{
2652100878Syar	char *s;
26531592Srgrimes
26541592Srgrimes	LOGCMD("mkdir", name);
265599195Smdodd	if (guest && noguestmkd)
2656137853Syar		reply(550, "Operation not permitted.");
265799195Smdodd	else if (mkdir(name, 0777) < 0)
26581592Srgrimes		perror_reply(550, name);
2659100878Syar	else {
2660100878Syar		if ((s = doublequote(name)) == NULL)
2661100878Syar			fatalerror("Ran out of memory.");
2662100878Syar		reply(257, "\"%s\" directory created.", s);
2663100878Syar		free(s);
2664100878Syar	}
26651592Srgrimes}
26661592Srgrimes
26671592Srgrimesvoid
266890148Simpremovedir(char *name)
26691592Srgrimes{
26701592Srgrimes
26711592Srgrimes	LOGCMD("rmdir", name);
26721592Srgrimes	if (rmdir(name) < 0)
26731592Srgrimes		perror_reply(550, name);
26741592Srgrimes	else
26751592Srgrimes		ack("RMD");
26761592Srgrimes}
26771592Srgrimes
26781592Srgrimesvoid
267990148Simppwd(void)
26801592Srgrimes{
2681100486Syar	char *s, path[MAXPATHLEN + 1];
26821592Srgrimes
2683137830Syar	if (getcwd(path, sizeof(path)) == NULL)
2684137839Syar		perror_reply(550, "Get current directory");
2685100486Syar	else {
2686100486Syar		if ((s = doublequote(path)) == NULL)
2687100486Syar			fatalerror("Ran out of memory.");
2688100486Syar		reply(257, "\"%s\" is current directory.", s);
2689100486Syar		free(s);
2690100486Syar	}
26911592Srgrimes}
26921592Srgrimes
26931592Srgrimeschar *
269490148Simprenamefrom(char *name)
26951592Srgrimes{
26961592Srgrimes	struct stat st;
26971592Srgrimes
2698125569Syar	if (guest && noguestmod) {
2699137852Syar		reply(550, "Operation not permitted.");
2700125569Syar		return (NULL);
2701125569Syar	}
2702100439Syar	if (lstat(name, &st) < 0) {
27031592Srgrimes		perror_reply(550, name);
2704125570Syar		return (NULL);
27051592Srgrimes	}
2706137852Syar	reply(350, "File exists, ready for destination name.");
27071592Srgrimes	return (name);
27081592Srgrimes}
27091592Srgrimes
27101592Srgrimesvoid
271190148Simprenamecmd(char *from, char *to)
27121592Srgrimes{
271317433Spst	struct stat st;
27141592Srgrimes
27151592Srgrimes	LOGCMD2("rename", from, to);
271617433Spst
271717433Spst	if (guest && (stat(to, &st) == 0)) {
2718137852Syar		reply(550, "%s: permission denied.", to);
271917433Spst		return;
272017433Spst	}
272117433Spst
27221592Srgrimes	if (rename(from, to) < 0)
27231592Srgrimes		perror_reply(550, "rename");
27241592Srgrimes	else
27251592Srgrimes		ack("RNTO");
27261592Srgrimes}
27271592Srgrimes
27281592Srgrimesstatic void
272990148Simpdolog(struct sockaddr *who)
27301592Srgrimes{
2731137984Syar	char who_name[NI_MAXHOST];
27321592Srgrimes
273356668Sshin	realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len);
2734137983Syar	remotehost[sizeof(remotehost) - 1] = 0;
2735137984Syar	if (getnameinfo(who, who->sa_len,
2736137984Syar		who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST))
2737137984Syar			*who_name = 0;
2738137984Syar	who_name[sizeof(who_name) - 1] = 0;
273956668Sshin
27401592Srgrimes#ifdef SETPROCTITLE
274125283Sdavidn#ifdef VIRTUAL_HOSTING
274225283Sdavidn	if (thishost != firsthost)
274325283Sdavidn		snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)",
274425283Sdavidn			 remotehost, hostname);
274525283Sdavidn	else
274625283Sdavidn#endif
274725283Sdavidn		snprintf(proctitle, sizeof(proctitle), "%s: connected",
274825283Sdavidn			 remotehost);
274913139Speter	setproctitle("%s", proctitle);
27501592Srgrimes#endif /* SETPROCTITLE */
27511592Srgrimes
275225283Sdavidn	if (logging) {
275325283Sdavidn#ifdef VIRTUAL_HOSTING
275425283Sdavidn		if (thishost != firsthost)
2755137984Syar			syslog(LOG_INFO, "connection from %s (%s) to %s",
2756137984Syar			       remotehost, who_name, hostname);
275725283Sdavidn		else
275825283Sdavidn#endif
2759137984Syar			syslog(LOG_INFO, "connection from %s (%s)",
2760137984Syar			       remotehost, who_name);
276125283Sdavidn	}
27621592Srgrimes}
27631592Srgrimes
27641592Srgrimes/*
27651592Srgrimes * Record logout in wtmp file
27661592Srgrimes * and exit with supplied status.
27671592Srgrimes */
27681592Srgrimesvoid
276990148Simpdologout(int status)
27701592Srgrimes{
27711592Srgrimes
2772202604Sed	if (logged_in && dowtmp) {
2773132893Syar		(void) seteuid(0);
2774202604Sed		ftpd_logwtmp(wtmpid, NULL, NULL);
27751592Srgrimes	}
27761592Srgrimes	/* beware of flushing buffers after a SIGPIPE */
27771592Srgrimes	_exit(status);
27781592Srgrimes}
27791592Srgrimes
27801592Srgrimesstatic void
278190148Simpsigurg(int signo)
27821592Srgrimes{
278389935Syar
278489935Syar	recvurg = 1;
278589935Syar}
278689935Syar
278789935Syarstatic void
2788140472Syarmaskurg(int flag)
2789140472Syar{
2790140472Syar	int oerrno;
2791140472Syar	sigset_t sset;
2792140472Syar
2793140472Syar	if (!transflag) {
2794140472Syar		syslog(LOG_ERR, "Internal: maskurg() while no transfer");
2795140472Syar		return;
2796140472Syar	}
2797140472Syar	oerrno = errno;
2798140472Syar	sigemptyset(&sset);
2799140472Syar	sigaddset(&sset, SIGURG);
2800140472Syar	sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL);
2801140472Syar	errno = oerrno;
2802140472Syar}
2803140472Syar
2804140472Syarstatic void
2805140472Syarflagxfer(int flag)
2806140472Syar{
2807140472Syar
2808140472Syar	if (flag) {
2809141967Syar		if (transflag)
2810141967Syar			syslog(LOG_ERR, "Internal: flagxfer(1): "
2811141967Syar					"transfer already under way");
2812141966Syar		transflag = 1;
2813141966Syar		maskurg(0);
2814140472Syar		recvurg = 0;
2815141966Syar	} else {
2816141967Syar		if (!transflag)
2817141967Syar			syslog(LOG_ERR, "Internal: flagxfer(0): "
2818141967Syar					"no active transfer");
2819141966Syar		maskurg(1);
2820140472Syar		transflag = 0;
2821141966Syar	}
2822140472Syar}
2823140472Syar
2824140472Syar/*
2825140472Syar * Returns 0 if OK to resume or -1 if abort requested.
2826140472Syar */
2827140472Syarstatic int
282890148Simpmyoob(void)
282989935Syar{
28301592Srgrimes	char *cp;
2831186405Scperciva	int ret;
28321592Srgrimes
2833140472Syar	if (!transflag) {
2834140472Syar		syslog(LOG_ERR, "Internal: myoob() while no transfer");
2835140472Syar		return (0);
2836140472Syar	}
28371592Srgrimes	cp = tmpline;
2838299356Sbapt	ret = get_line(cp, 7, stdin);
2839186405Scperciva	if (ret == -1) {
28401592Srgrimes		reply(221, "You could at least say goodbye.");
28411592Srgrimes		dologout(0);
2842186405Scperciva	} else if (ret == -2) {
2843186405Scperciva		/* Ignore truncated command. */
2844186405Scperciva		return (0);
28451592Srgrimes	}
28461592Srgrimes	upper(cp);
28471592Srgrimes	if (strcmp(cp, "ABOR\r\n") == 0) {
28481592Srgrimes		tmpline[0] = '\0';
28491592Srgrimes		reply(426, "Transfer aborted. Data connection closed.");
2850137852Syar		reply(226, "Abort successful.");
2851140472Syar		return (-1);
28521592Srgrimes	}
28531592Srgrimes	if (strcmp(cp, "STAT\r\n") == 0) {
285451192Smharo		tmpline[0] = '\0';
2855132930Syar		if (file_size != -1)
2856137852Syar			reply(213, "Status: %jd of %jd bytes transferred.",
2857132929Syar				   (intmax_t)byte_count, (intmax_t)file_size);
28581592Srgrimes		else
2859137852Syar			reply(213, "Status: %jd bytes transferred.",
2860132929Syar				   (intmax_t)byte_count);
28611592Srgrimes	}
2862140472Syar	return (0);
28631592Srgrimes}
28641592Srgrimes
28651592Srgrimes/*
28661592Srgrimes * Note: a response of 425 is not mentioned as a possible response to
28671592Srgrimes *	the PASV command in RFC959. However, it has been blessed as
28681592Srgrimes *	a legitimate response by Jon Postel in a telephone conversation
28691592Srgrimes *	with Rick Adams on 25 Jan 89.
28701592Srgrimes */
28711592Srgrimesvoid
287290148Simppassive(void)
28731592Srgrimes{
2874141918Sstefanf	socklen_t len;
2875141918Sstefanf	int on;
28761592Srgrimes	char *p, *a;
28771592Srgrimes
287817433Spst	if (pdata >= 0)		/* close old port if one set */
287917433Spst		close(pdata);
288017433Spst
288156668Sshin	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
28821592Srgrimes	if (pdata < 0) {
28831592Srgrimes		perror_reply(425, "Can't open passive connection");
28841592Srgrimes		return;
28851592Srgrimes	}
2886100615Syar	on = 1;
2887100615Syar	if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
2888100615Syar		syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m");
28899933Spst
2890132893Syar	(void) seteuid(0);
289117433Spst
289219903Spst#ifdef IP_PORTRANGE
289356668Sshin	if (ctrl_addr.su_family == AF_INET) {
2894100615Syar	    on = restricted_data_ports ? IP_PORTRANGE_HIGH
2895100615Syar				       : IP_PORTRANGE_DEFAULT;
289619903Spst
289719903Spst	    if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
2898100612Syar			    &on, sizeof(on)) < 0)
289919903Spst		    goto pasv_error;
29001592Srgrimes	}
290119903Spst#endif
290260929Snsayer#ifdef IPV6_PORTRANGE
290360929Snsayer	if (ctrl_addr.su_family == AF_INET6) {
2904100615Syar	    on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
2905100615Syar				       : IPV6_PORTRANGE_DEFAULT;
29069933Spst
290760929Snsayer	    if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
2908100612Syar			    &on, sizeof(on)) < 0)
290960929Snsayer		    goto pasv_error;
291060929Snsayer	}
291160929Snsayer#endif
291260929Snsayer
291316033Speter	pasv_addr = ctrl_addr;
291456668Sshin	pasv_addr.su_port = 0;
291556668Sshin	if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
291616033Speter		goto pasv_error;
291717433Spst
2918132893Syar	(void) seteuid(pw->pw_uid);
291916033Speter
29201592Srgrimes	len = sizeof(pasv_addr);
29211592Srgrimes	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
29221592Srgrimes		goto pasv_error;
29231592Srgrimes	if (listen(pdata, 1) < 0)
29241592Srgrimes		goto pasv_error;
292556668Sshin	if (pasv_addr.su_family == AF_INET)
292656668Sshin		a = (char *) &pasv_addr.su_sin.sin_addr;
292756668Sshin	else if (pasv_addr.su_family == AF_INET6 &&
292856668Sshin		 IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr))
292956668Sshin		a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
293056668Sshin	else
293156668Sshin		goto pasv_error;
293256668Sshin
293356668Sshin	p = (char *) &pasv_addr.su_port;
29341592Srgrimes
29351592Srgrimes#define UC(b) (((int) b) & 0xff)
29361592Srgrimes
29371592Srgrimes	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
29381592Srgrimes		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
29391592Srgrimes	return;
29401592Srgrimes
29411592Srgrimespasv_error:
2942132893Syar	(void) seteuid(pw->pw_uid);
29431592Srgrimes	(void) close(pdata);
29441592Srgrimes	pdata = -1;
29451592Srgrimes	perror_reply(425, "Can't open passive connection");
29461592Srgrimes	return;
29471592Srgrimes}
29481592Srgrimes
29491592Srgrimes/*
295056668Sshin * Long Passive defined in RFC 1639.
295156668Sshin *     228 Entering Long Passive Mode
295256668Sshin *         (af, hal, h1, h2, h3,..., pal, p1, p2...)
295356668Sshin */
295456668Sshin
295556668Sshinvoid
295690148Simplong_passive(char *cmd, int pf)
295756668Sshin{
2958141918Sstefanf	socklen_t len;
2959141918Sstefanf	int on;
296056668Sshin	char *p, *a;
296156668Sshin
296256668Sshin	if (pdata >= 0)		/* close old port if one set */
296356668Sshin		close(pdata);
296456668Sshin
296556668Sshin	if (pf != PF_UNSPEC) {
296656668Sshin		if (ctrl_addr.su_family != pf) {
296756668Sshin			switch (ctrl_addr.su_family) {
296856668Sshin			case AF_INET:
296956668Sshin				pf = 1;
297056668Sshin				break;
297156668Sshin			case AF_INET6:
297256668Sshin				pf = 2;
297356668Sshin				break;
297456668Sshin			default:
297556668Sshin				pf = 0;
297656668Sshin				break;
297756668Sshin			}
297856668Sshin			/*
297956668Sshin			 * XXX
298056668Sshin			 * only EPRT/EPSV ready clients will understand this
298156668Sshin			 */
298256668Sshin			if (strcmp(cmd, "EPSV") == 0 && pf) {
298356668Sshin				reply(522, "Network protocol mismatch, "
298456668Sshin					"use (%d)", pf);
298556668Sshin			} else
2986137852Syar				reply(501, "Network protocol mismatch."); /*XXX*/
298756668Sshin
298856668Sshin			return;
298956668Sshin		}
299056668Sshin	}
299156668Sshin
299256668Sshin	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
299356668Sshin	if (pdata < 0) {
299456668Sshin		perror_reply(425, "Can't open passive connection");
299556668Sshin		return;
299656668Sshin	}
2997100615Syar	on = 1;
2998100615Syar	if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
2999100615Syar		syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m");
300056668Sshin
3001132893Syar	(void) seteuid(0);
300256668Sshin
300356668Sshin	pasv_addr = ctrl_addr;
300456668Sshin	pasv_addr.su_port = 0;
300556668Sshin	len = pasv_addr.su_len;
300656668Sshin
300760929Snsayer#ifdef IP_PORTRANGE
300860929Snsayer	if (ctrl_addr.su_family == AF_INET) {
3009100615Syar	    on = restricted_data_ports ? IP_PORTRANGE_HIGH
3010100615Syar				       : IP_PORTRANGE_DEFAULT;
301160929Snsayer
301260929Snsayer	    if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
3013100612Syar			    &on, sizeof(on)) < 0)
301460929Snsayer		    goto pasv_error;
301560929Snsayer	}
301660929Snsayer#endif
301760929Snsayer#ifdef IPV6_PORTRANGE
301860929Snsayer	if (ctrl_addr.su_family == AF_INET6) {
3019100615Syar	    on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
3020100615Syar				       : IPV6_PORTRANGE_DEFAULT;
302160929Snsayer
302260929Snsayer	    if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
3023100612Syar			    &on, sizeof(on)) < 0)
302460929Snsayer		    goto pasv_error;
302560929Snsayer	}
302660929Snsayer#endif
302760929Snsayer
302856668Sshin	if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0)
302956668Sshin		goto pasv_error;
303056668Sshin
3031132893Syar	(void) seteuid(pw->pw_uid);
303256668Sshin
303356668Sshin	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
303456668Sshin		goto pasv_error;
303556668Sshin	if (listen(pdata, 1) < 0)
303656668Sshin		goto pasv_error;
303756668Sshin
303856668Sshin#define UC(b) (((int) b) & 0xff)
303956668Sshin
304056668Sshin	if (strcmp(cmd, "LPSV") == 0) {
304156668Sshin		p = (char *)&pasv_addr.su_port;
304256668Sshin		switch (pasv_addr.su_family) {
304356668Sshin		case AF_INET:
304456668Sshin			a = (char *) &pasv_addr.su_sin.sin_addr;
304556668Sshin		v4_reply:
304656668Sshin			reply(228,
304756668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
304856668Sshin			      4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
304956668Sshin			      2, UC(p[0]), UC(p[1]));
305056668Sshin			return;
305156668Sshin		case AF_INET6:
305256668Sshin			if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) {
305356668Sshin				a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
305456668Sshin				goto v4_reply;
305556668Sshin			}
305656668Sshin			a = (char *) &pasv_addr.su_sin6.sin6_addr;
305756668Sshin			reply(228,
305856668Sshin"Entering Long Passive Mode "
305956668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
306056668Sshin			      6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
306156668Sshin			      UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
306256668Sshin			      UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
306356668Sshin			      UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
306456668Sshin			      2, UC(p[0]), UC(p[1]));
306556668Sshin			return;
306656668Sshin		}
306756668Sshin	} else if (strcmp(cmd, "EPSV") == 0) {
306856668Sshin		switch (pasv_addr.su_family) {
306956668Sshin		case AF_INET:
307056668Sshin		case AF_INET6:
307156668Sshin			reply(229, "Entering Extended Passive Mode (|||%d|)",
307256668Sshin				ntohs(pasv_addr.su_port));
307356668Sshin			return;
307456668Sshin		}
307556668Sshin	} else {
307656668Sshin		/* more proper error code? */
307756668Sshin	}
307856668Sshin
307956668Sshinpasv_error:
3080132893Syar	(void) seteuid(pw->pw_uid);
308156668Sshin	(void) close(pdata);
308256668Sshin	pdata = -1;
308356668Sshin	perror_reply(425, "Can't open passive connection");
308456668Sshin	return;
308556668Sshin}
308656668Sshin
308756668Sshin/*
3088101537Syar * Generate unique name for file with basename "local"
3089101537Syar * and open the file in order to avoid possible races.
3090101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99".
3091101537Syar * Return descriptor to the file, set "name" to its name.
3092101537Syar *
30931592Srgrimes * Generates failure reply on error.
30941592Srgrimes */
3095101537Syarstatic int
3096101537Syarguniquefd(char *local, char **name)
30971592Srgrimes{
30981592Srgrimes	static char new[MAXPATHLEN];
30991592Srgrimes	struct stat st;
3100101537Syar	char *cp;
31011592Srgrimes	int count;
3102101537Syar	int fd;
31031592Srgrimes
31041592Srgrimes	cp = strrchr(local, '/');
31051592Srgrimes	if (cp)
31061592Srgrimes		*cp = '\0';
31071592Srgrimes	if (stat(cp ? local : ".", &st) < 0) {
31081592Srgrimes		perror_reply(553, cp ? local : ".");
3109101537Syar		return (-1);
31101592Srgrimes	}
3111101537Syar	if (cp) {
3112101537Syar		/*
3113101537Syar		 * Let not overwrite dirname with counter suffix.
3114101537Syar		 * -4 is for /nn\0
3115101537Syar		 * In this extreme case dot won't be put in front of suffix.
3116101537Syar		 */
3117101537Syar		if (strlen(local) > sizeof(new) - 4) {
3118137852Syar			reply(553, "Pathname too long.");
3119101537Syar			return (-1);
3120101537Syar		}
31211592Srgrimes		*cp = '/';
3122101537Syar	}
312331973Simp	/* -4 is for the .nn<null> we put on the end below */
312431973Simp	(void) snprintf(new, sizeof(new) - 4, "%s", local);
31251592Srgrimes	cp = new + strlen(new);
3126101537Syar	/*
3127101537Syar	 * Don't generate dotfile unless requested explicitly.
3128101537Syar	 * This covers the case when basename gets truncated off
3129101537Syar	 * by buffer size.
3130101537Syar	 */
3131101537Syar	if (cp > new && cp[-1] != '/')
3132101537Syar		*cp++ = '.';
3133101537Syar	for (count = 0; count < 100; count++) {
3134101537Syar		/* At count 0 try unmodified name */
3135101537Syar		if (count)
3136101537Syar			(void)sprintf(cp, "%d", count);
3137101537Syar		if ((fd = open(count ? new : local,
3138101537Syar		    O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) {
3139101537Syar			*name = count ? new : local;
3140101537Syar			return (fd);
3141101537Syar		}
3142110046Syar		if (errno != EEXIST) {
3143110307Syar			perror_reply(553, count ? new : local);
3144110046Syar			return (-1);
3145110046Syar		}
31461592Srgrimes	}
31471592Srgrimes	reply(452, "Unique file name cannot be created.");
3148101537Syar	return (-1);
31491592Srgrimes}
31501592Srgrimes
31511592Srgrimes/*
31521592Srgrimes * Format and send reply containing system error number.
31531592Srgrimes */
31541592Srgrimesvoid
315590148Simpperror_reply(int code, char *string)
31561592Srgrimes{
31571592Srgrimes
31581592Srgrimes	reply(code, "%s: %s.", string, strerror(errno));
31591592Srgrimes}
31601592Srgrimes
31611592Srgrimesstatic char *onefile[] = {
31621592Srgrimes	"",
31631592Srgrimes	0
31641592Srgrimes};
31651592Srgrimes
31661592Srgrimesvoid
316790148Simpsend_file_list(char *whichf)
31681592Srgrimes{
31691592Srgrimes	struct stat st;
31701592Srgrimes	DIR *dirp = NULL;
31711592Srgrimes	struct dirent *dir;
31721592Srgrimes	FILE *dout = NULL;
31731592Srgrimes	char **dirlist, *dirname;
31741592Srgrimes	int simple = 0;
31751592Srgrimes	int freeglob = 0;
31761592Srgrimes	glob_t gl;
31771592Srgrimes
31781592Srgrimes	if (strpbrk(whichf, "~{[*?") != NULL) {
3179100222Smikeh		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
31801592Srgrimes
31811592Srgrimes		memset(&gl, 0, sizeof(gl));
318274470Sjlemon		gl.gl_matchc = MAXGLOBARGS;
318380525Smikeh		flags |= GLOB_LIMIT;
31841592Srgrimes		freeglob = 1;
31851592Srgrimes		if (glob(whichf, flags, 0, &gl)) {
3186137852Syar			reply(550, "No matching files found.");
31871592Srgrimes			goto out;
31881592Srgrimes		} else if (gl.gl_pathc == 0) {
31891592Srgrimes			errno = ENOENT;
31901592Srgrimes			perror_reply(550, whichf);
31911592Srgrimes			goto out;
31921592Srgrimes		}
31931592Srgrimes		dirlist = gl.gl_pathv;
31941592Srgrimes	} else {
31951592Srgrimes		onefile[0] = whichf;
31961592Srgrimes		dirlist = onefile;
31971592Srgrimes		simple = 1;
31981592Srgrimes	}
31991592Srgrimes
320017478Smarkm	while ((dirname = *dirlist++)) {
32011592Srgrimes		if (stat(dirname, &st) < 0) {
32021592Srgrimes			/*
32031592Srgrimes			 * If user typed "ls -l", etc, and the client
32041592Srgrimes			 * used NLST, do what the user meant.
32051592Srgrimes			 */
32061592Srgrimes			if (dirname[0] == '-' && *dirlist == NULL &&
3207140472Syar			    dout == NULL)
320825165Sdavidn				retrieve(_PATH_LS " %s", dirname);
3209140472Syar			else
3210140472Syar				perror_reply(550, whichf);
32111592Srgrimes			goto out;
32121592Srgrimes		}
32131592Srgrimes
32141592Srgrimes		if (S_ISREG(st.st_mode)) {
32151592Srgrimes			if (dout == NULL) {
3216132930Syar				dout = dataconn("file list", -1, "w");
32171592Srgrimes				if (dout == NULL)
32181592Srgrimes					goto out;
3219140472Syar				STARTXFER;
32201592Srgrimes			}
3221140472Syar			START_UNSAFE;
32221592Srgrimes			fprintf(dout, "%s%s\n", dirname,
32231592Srgrimes				type == TYPE_A ? "\r" : "");
3224140472Syar			END_UNSAFE;
3225140472Syar			if (ferror(dout))
3226140472Syar				goto data_err;
3227140472Syar			byte_count += strlen(dirname) +
3228140472Syar				      (type == TYPE_A ? 2 : 1);
3229140472Syar			CHECKOOB(goto abrt);
32301592Srgrimes			continue;
32311592Srgrimes		} else if (!S_ISDIR(st.st_mode))
32321592Srgrimes			continue;
32331592Srgrimes
32341592Srgrimes		if ((dirp = opendir(dirname)) == NULL)
32351592Srgrimes			continue;
32361592Srgrimes
32371592Srgrimes		while ((dir = readdir(dirp)) != NULL) {
32381592Srgrimes			char nbuf[MAXPATHLEN];
32391592Srgrimes
3240140472Syar			CHECKOOB(goto abrt);
324189935Syar
32421592Srgrimes			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
32431592Srgrimes				continue;
32441592Srgrimes			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
32451592Srgrimes			    dir->d_namlen == 2)
32461592Srgrimes				continue;
32471592Srgrimes
324899213Smaxim			snprintf(nbuf, sizeof(nbuf),
324931973Simp				"%s/%s", dirname, dir->d_name);
32501592Srgrimes
32511592Srgrimes			/*
32521592Srgrimes			 * We have to do a stat to insure it's
32531592Srgrimes			 * not a directory or special file.
32541592Srgrimes			 */
32551592Srgrimes			if (simple || (stat(nbuf, &st) == 0 &&
32561592Srgrimes			    S_ISREG(st.st_mode))) {
32571592Srgrimes				if (dout == NULL) {
3258132930Syar					dout = dataconn("file list", -1, "w");
32591592Srgrimes					if (dout == NULL)
32601592Srgrimes						goto out;
3261140472Syar					STARTXFER;
32621592Srgrimes				}
3263140472Syar				START_UNSAFE;
32641592Srgrimes				if (nbuf[0] == '.' && nbuf[1] == '/')
32651592Srgrimes					fprintf(dout, "%s%s\n", &nbuf[2],
32661592Srgrimes						type == TYPE_A ? "\r" : "");
32671592Srgrimes				else
32681592Srgrimes					fprintf(dout, "%s%s\n", nbuf,
32691592Srgrimes						type == TYPE_A ? "\r" : "");
3270140472Syar				END_UNSAFE;
3271140472Syar				if (ferror(dout))
3272140472Syar					goto data_err;
3273140472Syar				byte_count += strlen(nbuf) +
3274140472Syar					      (type == TYPE_A ? 2 : 1);
3275140472Syar				CHECKOOB(goto abrt);
32761592Srgrimes			}
32771592Srgrimes		}
32781592Srgrimes		(void) closedir(dirp);
3279140472Syar		dirp = NULL;
32801592Srgrimes	}
32811592Srgrimes
32821592Srgrimes	if (dout == NULL)
32831592Srgrimes		reply(550, "No files found.");
3284140472Syar	else if (ferror(dout))
3285140472Syardata_err:	perror_reply(550, "Data connection");
32861592Srgrimes	else
32871592Srgrimes		reply(226, "Transfer complete.");
3288140472Syarout:
3289140472Syar	if (dout) {
3290140472Syar		ENDXFER;
3291140472Syarabrt:
32921592Srgrimes		(void) fclose(dout);
3293140472Syar		data = -1;
3294140472Syar		pdata = -1;
3295140472Syar	}
3296140472Syar	if (dirp)
3297140472Syar		(void) closedir(dirp);
32981592Srgrimes	if (freeglob) {
32991592Srgrimes		freeglob = 0;
33001592Srgrimes		globfree(&gl);
33011592Srgrimes	}
33021592Srgrimes}
33031592Srgrimes
330415196Sdgvoid
330590148Simpreapchild(int signo)
330615196Sdg{
3307137830Syar	while (waitpid(-1, NULL, WNOHANG) > 0);
330815196Sdg}
330915196Sdg
331013139Speter#ifdef OLD_SETPROCTITLE
33111592Srgrimes/*
33121592Srgrimes * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
33131592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't
33141592Srgrimes * have much of an environment or arglist to overwrite.
33151592Srgrimes */
33161592Srgrimesvoid
33171592Srgrimessetproctitle(const char *fmt, ...)
33181592Srgrimes{
33191592Srgrimes	int i;
33201592Srgrimes	va_list ap;
33211592Srgrimes	char *p, *bp, ch;
33221592Srgrimes	char buf[LINE_MAX];
33231592Srgrimes
33241592Srgrimes	va_start(ap, fmt);
33251592Srgrimes	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
33261592Srgrimes
33271592Srgrimes	/* make ps print our process name */
33281592Srgrimes	p = Argv[0];
33291592Srgrimes	*p++ = '-';
33301592Srgrimes
33311592Srgrimes	i = strlen(buf);
33321592Srgrimes	if (i > LastArgv - p - 2) {
33331592Srgrimes		i = LastArgv - p - 2;
33341592Srgrimes		buf[i] = '\0';
33351592Srgrimes	}
33361592Srgrimes	bp = buf;
33371592Srgrimes	while (ch = *bp++)
33381592Srgrimes		if (ch != '\n' && ch != '\r')
33391592Srgrimes			*p++ = ch;
33401592Srgrimes	while (p < LastArgv)
33411592Srgrimes		*p++ = ' ';
33421592Srgrimes}
334313139Speter#endif /* OLD_SETPROCTITLE */
33446740Sguido
334517433Spststatic void
3346137848Syarappendf(char **strp, char *fmt, ...)
3347137848Syar{
3348137848Syar	va_list ap;
3349137848Syar	char *ostr, *p;
3350137848Syar
3351137848Syar	va_start(ap, fmt);
3352137848Syar	vasprintf(&p, fmt, ap);
3353137848Syar	va_end(ap);
3354137848Syar	if (p == NULL)
3355137848Syar		fatalerror("Ran out of memory.");
3356137848Syar	if (*strp == NULL)
3357137848Syar		*strp = p;
3358137848Syar	else {
3359137848Syar		ostr = *strp;
3360137848Syar		asprintf(strp, "%s%s", ostr, p);
3361137848Syar		if (*strp == NULL)
3362137848Syar			fatalerror("Ran out of memory.");
3363137848Syar		free(ostr);
3364137848Syar	}
3365137848Syar}
3366137848Syar
3367137848Syarstatic void
3368137848Syarlogcmd(char *cmd, char *file1, char *file2, off_t cnt)
3369137848Syar{
3370137848Syar	char *msg = NULL;
3371137848Syar	char wd[MAXPATHLEN + 1];
3372137848Syar
3373137848Syar	if (logging <= 1)
3374137848Syar		return;
3375137848Syar
3376137985Syar	if (getcwd(wd, sizeof(wd) - 1) == NULL)
3377137985Syar		strcpy(wd, strerror(errno));
3378137985Syar
3379137848Syar	appendf(&msg, "%s", cmd);
3380137848Syar	if (file1)
3381137848Syar		appendf(&msg, " %s", file1);
3382137848Syar	if (file2)
3383137848Syar		appendf(&msg, " %s", file2);
3384137848Syar	if (cnt >= 0)
3385137848Syar		appendf(&msg, " = %jd bytes", (intmax_t)cnt);
3386137985Syar	appendf(&msg, " (wd: %s", wd);
3387137862Syar	if (guest || dochroot)
3388137985Syar		appendf(&msg, "; chrooted");
3389137985Syar	appendf(&msg, ")");
3390137848Syar	syslog(LOG_INFO, "%s", msg);
3391137848Syar	free(msg);
3392137848Syar}
3393137848Syar
3394137848Syarstatic void
339590148Simplogxfer(char *name, off_t size, time_t start)
33966740Sguido{
3397137145Syar	char buf[MAXPATHLEN + 1024];
33986740Sguido	char path[MAXPATHLEN + 1];
339936612Sjb	time_t now;
34006740Sguido
3401137145Syar	if (statfd >= 0) {
34026740Sguido		time(&now);
3403137145Syar		if (realpath(name, path) == NULL) {
3404137145Syar			syslog(LOG_NOTICE, "realpath failed on %s: %m", path);
3405137145Syar			return;
3406137145Syar		}
3407137145Syar		snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n",
34086740Sguido			ctime(&now)+4, ident, remotehost,
3409137145Syar			path, (intmax_t)size,
341082792Sache			(long)(now - start + (now == start)));
34116740Sguido		write(statfd, buf, strlen(buf));
34126740Sguido	}
34136740Sguido}
3414100486Syar
3415100486Syarstatic char *
3416100486Syardoublequote(char *s)
3417100486Syar{
3418100486Syar	int n;
3419100486Syar	char *p, *s2;
3420100486Syar
3421100486Syar	for (p = s, n = 0; *p; p++)
3422100486Syar		if (*p == '"')
3423100486Syar			n++;
3424100486Syar
3425100486Syar	if ((s2 = malloc(p - s + n + 1)) == NULL)
3426100486Syar		return (NULL);
3427100486Syar
3428100486Syar	for (p = s2; *s; s++, p++) {
3429100486Syar		if ((*p = *s) == '"')
3430100486Syar			*(++p) = '"';
3431100486Syar	}
3432100486Syar	*p = '\0';
3433100486Syar
3434100486Syar	return (s2);
3435100486Syar}
3436120059Sume
3437120059Sume/* setup server socket for specified address family */
3438120059Sume/* if af is PF_UNSPEC more than one socket may be returned */
3439120059Sume/* the returned list is dynamically allocated, so caller needs to free it */
3440120059Sumestatic int *
3441120059Sumesocksetup(int af, char *bindname, const char *bindport)
3442120059Sume{
3443120059Sume	struct addrinfo hints, *res, *r;
3444120059Sume	int error, maxs, *s, *socks;
3445120059Sume	const int on = 1;
3446120059Sume
3447120059Sume	memset(&hints, 0, sizeof(hints));
3448120059Sume	hints.ai_flags = AI_PASSIVE;
3449120059Sume	hints.ai_family = af;
3450120059Sume	hints.ai_socktype = SOCK_STREAM;
3451120059Sume	error = getaddrinfo(bindname, bindport, &hints, &res);
3452120059Sume	if (error) {
3453120059Sume		syslog(LOG_ERR, "%s", gai_strerror(error));
3454120059Sume		if (error == EAI_SYSTEM)
3455120059Sume			syslog(LOG_ERR, "%s", strerror(errno));
3456120059Sume		return NULL;
3457120059Sume	}
3458120059Sume
3459120059Sume	/* Count max number of sockets we may open */
3460120059Sume	for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
3461120059Sume		;
3462120059Sume	socks = malloc((maxs + 1) * sizeof(int));
3463120059Sume	if (!socks) {
3464120059Sume		freeaddrinfo(res);
3465120059Sume		syslog(LOG_ERR, "couldn't allocate memory for sockets");
3466120059Sume		return NULL;
3467120059Sume	}
3468120059Sume
3469120059Sume	*socks = 0;   /* num of sockets counter at start of array */
3470120059Sume	s = socks + 1;
3471120059Sume	for (r = res; r; r = r->ai_next) {
3472120059Sume		*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
3473120059Sume		if (*s < 0) {
3474120059Sume			syslog(LOG_DEBUG, "control socket: %m");
3475120059Sume			continue;
3476120059Sume		}
3477120059Sume		if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR,
3478120059Sume		    &on, sizeof(on)) < 0)
3479120059Sume			syslog(LOG_WARNING,
3480120059Sume			    "control setsockopt (SO_REUSEADDR): %m");
3481120059Sume		if (r->ai_family == AF_INET6) {
3482120059Sume			if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
3483120059Sume			    &on, sizeof(on)) < 0)
3484120059Sume				syslog(LOG_WARNING,
3485120059Sume				    "control setsockopt (IPV6_V6ONLY): %m");
3486120059Sume		}
3487120059Sume		if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
3488120059Sume			syslog(LOG_DEBUG, "control bind: %m");
3489120059Sume			close(*s);
3490120059Sume			continue;
3491120059Sume		}
3492120059Sume		(*socks)++;
3493120059Sume		s++;
3494120059Sume	}
3495120059Sume
3496120059Sume	if (res)
3497120059Sume		freeaddrinfo(res);
3498120059Sume
3499120059Sume	if (*socks == 0) {
3500120059Sume		syslog(LOG_ERR, "control socket: Couldn't bind to any socket");
3501120059Sume		free(socks);
3502120059Sume		return NULL;
3503120059Sume	}
3504120059Sume	return(socks);
3505120059Sume}
3506