1/*	$NetBSD: ftpd.c,v 1.187 2008/09/13 03:30:35 lukem Exp $	*/
2
3/*
4 * Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *        This product includes software developed by the NetBSD
21 *        Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 *    contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
41 *	The Regents of the University of California.  All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 *    notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 *    notice, this list of conditions and the following disclaimer in the
50 *    documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 *    may be used to endorse or promote products derived from this software
53 *    without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 */
67
68/*
69 * Copyright (C) 1997 and 1998 WIDE Project.
70 * All rights reserved.
71 *
72 * Redistribution and use in source and binary forms, with or without
73 * modification, are permitted provided that the following conditions
74 * are met:
75 * 1. Redistributions of source code must retain the above copyright
76 *    notice, this list of conditions and the following disclaimer.
77 * 2. Redistributions in binary form must reproduce the above copyright
78 *    notice, this list of conditions and the following disclaimer in the
79 *    documentation and/or other materials provided with the distribution.
80 * 3. Neither the name of the project nor the names of its contributors
81 *    may be used to endorse or promote products derived from this software
82 *    without specific prior written permission.
83 *
84 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
85 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
86 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
87 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
88 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
89 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
90 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
91 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
92 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
93 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94 * SUCH DAMAGE.
95 */
96
97#include <sys/cdefs.h>
98#ifndef lint
99__COPYRIGHT(
100"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
101	The Regents of the University of California.  All rights reserved.\n");
102#endif /* not lint */
103
104#ifndef lint
105#if 0
106static char sccsid[] = "@(#)ftpd.c	8.5 (Berkeley) 4/28/95";
107#else
108__RCSID("$NetBSD: ftpd.c,v 1.176 2006/05/09 20:18:06 mrg Exp $");
109#endif
110__FBSDID("$FreeBSD$");
111#endif /* not lint */
112
113/*
114 * FTP server.
115 */
116#include <sys/param.h>
117#include <sys/stat.h>
118#include <sys/ioctl.h>
119#include <sys/socket.h>
120#include <sys/wait.h>
121#include <sys/mman.h>
122#include <sys/resource.h>
123
124#include <netinet/in.h>
125#include <netinet/in_systm.h>
126#include <netinet/ip.h>
127
128#define	FTP_NAMES
129#include <arpa/ftp.h>
130#include <arpa/inet.h>
131#include <arpa/telnet.h>
132
133#include <ctype.h>
134#include <dirent.h>
135#include <err.h>
136#include <errno.h>
137#include <fcntl.h>
138#include <fnmatch.h>
139#include <glob.h>
140#include <grp.h>
141#include <limits.h>
142#include <netdb.h>
143#include <pwd.h>
144#include <poll.h>
145#include <signal.h>
146#include <stdarg.h>
147#include <stdio.h>
148#include <stdlib.h>
149#include <string.h>
150#include <syslog.h>
151#include <time.h>
152#include <tzfile.h>
153#include <unistd.h>
154#include <util.h>
155#ifdef SUPPORT_UTMP
156#include <utmp.h>
157#endif
158#ifdef SUPPORT_UTMPX
159#include <utmpx.h>
160#endif
161#ifdef SKEY
162#include <skey.h>
163#endif
164#ifdef KERBEROS5
165#include <com_err.h>
166#include <krb5/krb5.h>
167#endif
168
169#ifdef	LOGIN_CAP
170#include <login_cap.h>
171#endif
172
173#ifdef USE_PAM
174#include <security/pam_appl.h>
175#endif
176
177#define	GLOBAL
178#include "extern.h"
179#include "pathnames.h"
180#include "version.h"
181
182#include "nbsd_pidfile.h"
183
184volatile sig_atomic_t	transflag;
185volatile sig_atomic_t	urgflag;
186
187int	data;
188int	Dflag;
189int	sflag;
190int	stru;			/* avoid C keyword */
191int	mode;
192int	dataport;		/* use specific data port */
193int	dopidfile;		/* maintain pid file */
194int	doutmp;			/* update utmp file */
195int	dowtmp;			/* update wtmp file */
196int	doxferlog;		/* syslog/write wu-ftpd style xferlog entries */
197int	xferlogfd;		/* fd to write wu-ftpd xferlog entries to */
198int	dropprivs;		/* if privileges should or have been dropped */
199int	mapped;			/* IPv4 connection on AF_INET6 socket */
200off_t	file_size;
201off_t	byte_count;
202static char ttyline[20];
203
204#ifdef USE_PAM
205static int	auth_pam(struct passwd **, const char *);
206pam_handle_t	*pamh = NULL;
207#endif
208
209#ifdef SUPPORT_UTMP
210static struct utmp utmp;	/* for utmp */
211#endif
212#ifdef SUPPORT_UTMPX
213static struct utmpx utmpx;	/* for utmpx */
214#endif
215
216static const char *anondir = NULL;
217static const char *confdir = _DEFAULT_CONFDIR;
218
219static char	*curname;		/* current USER name */
220static size_t	curname_len;		/* length of curname (include NUL) */
221
222#if defined(KERBEROS) || defined(KERBEROS5)
223int	has_ccache = 0;
224int	notickets = 1;
225char	*krbtkfile_env = NULL;
226char	*tty = ttyline;
227int	login_krb5_forwardable_tgt = 0;
228#endif
229
230int epsvall = 0;
231
232/*
233 * Timeout intervals for retrying connections
234 * to hosts that don't accept PORT cmds.  This
235 * is a kludge, but given the problems with TCP...
236 */
237#define	SWAITMAX	90	/* wait at most 90 seconds */
238#define	SWAITINT	5	/* interval between retries */
239
240int	swaitmax = SWAITMAX;
241int	swaitint = SWAITINT;
242
243enum send_status {
244	SS_SUCCESS,
245	SS_ABORTED,			/* transfer aborted */
246	SS_NO_TRANSFER,			/* no transfer made yet */
247	SS_FILE_ERROR,			/* file read error */
248	SS_DATA_ERROR			/* data send error */
249};
250
251#ifdef USE_OPIE
252#include <opie.h>
253static struct opie opiedata;
254static char opieprompt[OPIE_CHALLENGE_MAX+1];
255static int pwok;
256#endif
257
258static int	 bind_pasv_addr(void);
259static int	 checkuser(const char *, const char *, int, int, char **);
260static int	 checkaccess(const char *);
261static int	 checkpassword(const struct passwd *, const char *);
262static void	 end_login(void);
263static FILE	*getdatasock(const char *);
264static char	*gunique(const char *);
265static void	 login_utmp(const char *, const char *, const char *,
266		     struct sockinet *);
267static void	 logremotehost(struct sockinet *);
268static void	 lostconn(int);
269static void	 toolong(int);
270static void	 sigquit(int);
271static void	 sigurg(int);
272static int	 handleoobcmd(void);
273static int	 receive_data(FILE *, FILE *);
274static int	 send_data(FILE *, FILE *, const struct stat *, int);
275static struct passwd *sgetpwnam(const char *);
276static int	 write_data(int, char *, size_t, off_t *, struct timeval *,
277		     int);
278static enum send_status
279		 send_data_with_read(int, int, const struct stat *, int);
280static enum send_status
281		 send_data_with_mmap(int, int, const struct stat *, int);
282static void	 logrusage(const struct rusage *, const struct rusage *);
283static void	 logout_utmp(void);
284
285int	main(int, char *[]);
286
287#if defined(KERBEROS)
288int	klogin(struct passwd *, char *, char *, char *);
289void	kdestroy(void);
290#endif
291#if defined(KERBEROS5)
292int	k5login(struct passwd *, char *, char *, char *);
293void	k5destroy(void);
294#endif
295
296int
297main(int argc, char *argv[])
298{
299	int		ch, on = 1, tos, keepalive;
300	socklen_t	addrlen;
301#ifdef KERBEROS5
302	krb5_error_code	kerror;
303#endif
304	char		*p;
305	const char	*xferlogname = NULL;
306	long		l;
307	struct sigaction sa;
308	sa_family_t	af = AF_UNSPEC;
309
310	connections = 1;
311	ftpd_debug = 0;
312	logging = 0;
313	pdata = -1;
314	Dflag = 0;
315	sflag = 0;
316	dataport = 0;
317	dopidfile = 1;		/* default: DO use a pid file to count users */
318	doutmp = 0;		/* default: Do NOT log to utmp */
319	dowtmp = 1;		/* default: DO log to wtmp */
320	doxferlog = 0;		/* default: Do NOT syslog xferlog */
321	xferlogfd = -1;		/* default: Do NOT write xferlog file */
322	dropprivs = 0;
323	mapped = 0;
324	usedefault = 1;
325	emailaddr = NULL;
326	hostname[0] = '\0';
327	homedir[0] = '\0';
328	gidcount = 0;
329	is_oob = 0;
330	version = FTPD_VERSION;
331
332	/*
333	 * LOG_NDELAY sets up the logging connection immediately,
334	 * necessary for anonymous ftp's that chroot and can't do it later.
335	 */
336	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
337
338	while ((ch = getopt(argc, argv,
339	    "46a:c:C:Dde:h:HlL:P:qQrst:T:uUvV:wWX")) != -1) {
340		switch (ch) {
341		case '4':
342			af = AF_INET;
343			break;
344
345		case '6':
346			af = AF_INET6;
347			break;
348
349		case 'a':
350			anondir = optarg;
351			break;
352
353		case 'c':
354			confdir = optarg;
355			break;
356
357		case 'C':
358			pw = sgetpwnam(optarg);
359			exit(checkaccess(optarg) ? 0 : 1);
360			/* NOTREACHED */
361
362		case 'D':
363			Dflag = 1;
364			break;
365
366		case 'd':
367		case 'v':		/* deprecated */
368			ftpd_debug = 1;
369			break;
370
371		case 'e':
372			emailaddr = optarg;
373			break;
374
375		case 'h':
376			strlcpy(hostname, optarg, sizeof(hostname));
377			break;
378
379		case 'H':
380			if (gethostname(hostname, sizeof(hostname)) == -1)
381				hostname[0] = '\0';
382			hostname[sizeof(hostname) - 1] = '\0';
383			break;
384
385		case 'l':
386			logging++;	/* > 1 == extra logging */
387			break;
388
389		case 'L':
390			xferlogname = optarg;
391			break;
392
393		case 'P':
394			errno = 0;
395			p = NULL;
396			l = strtol(optarg, &p, 10);
397			if (errno || *optarg == '\0' || *p != '\0' ||
398			    l < IPPORT_RESERVED ||
399			    l > IPPORT_ANONMAX) {
400				syslog(LOG_WARNING, "Invalid dataport %s",
401				    optarg);
402				dataport = 0;
403			}
404			dataport = (int)l;
405			break;
406
407		case 'q':
408			dopidfile = 1;
409			break;
410
411		case 'Q':
412			dopidfile = 0;
413			break;
414
415		case 'r':
416			dropprivs = 1;
417			break;
418
419		case 's':
420			sflag = 1;
421			break;
422
423		case 't':
424		case 'T':
425			syslog(LOG_WARNING,
426			    "-%c has been deprecated in favour of ftpd.conf",
427			    ch);
428			break;
429
430		case 'u':
431			doutmp = 1;
432			break;
433
434		case 'U':
435			doutmp = 0;
436			break;
437
438		case 'V':
439			if (EMPTYSTR(optarg) || strcmp(optarg, "-") == 0)
440				version = NULL;
441			else
442				version = ftpd_strdup(optarg);
443			break;
444
445		case 'w':
446			dowtmp = 1;
447			break;
448
449		case 'W':
450			dowtmp = 0;
451			break;
452
453		case 'X':
454			doxferlog |= 1;
455			break;
456
457		default:
458			if (optopt == 'a' || optopt == 'C')
459				exit(1);
460			syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
461			break;
462		}
463	}
464	if (EMPTYSTR(confdir))
465		confdir = _DEFAULT_CONFDIR;
466
467	if (dowtmp) {
468#ifdef SUPPORT_UTMPX
469		ftpd_initwtmpx();
470#endif
471#ifdef SUPPORT_UTMP
472		ftpd_initwtmp();
473#endif
474	}
475	errno = 0;
476	l = sysconf(_SC_LOGIN_NAME_MAX);
477	if (l == -1 && errno != 0) {
478		syslog(LOG_ERR, "sysconf _SC_LOGIN_NAME_MAX: %m");
479		exit(1);
480	} else if (l <= 0) {
481		syslog(LOG_WARNING, "using conservative LOGIN_NAME_MAX value");
482		curname_len = _POSIX_LOGIN_NAME_MAX;
483	} else
484		curname_len = (size_t)l;
485	curname = malloc(curname_len);
486	if (curname == NULL) {
487		syslog(LOG_ERR, "malloc: %m");
488		exit(1);
489	}
490	curname[0] = '\0';
491
492	if (Dflag) {
493		int error, fd, i, n, *socks;
494		struct pollfd *fds;
495		struct addrinfo hints, *res, *res0;
496
497		if (daemon(1, 0) == -1) {
498			syslog(LOG_ERR, "failed to daemonize: %m");
499			exit(1);
500		}
501		(void)memset(&sa, 0, sizeof(sa));
502		sa.sa_handler = SIG_IGN;
503		sa.sa_flags = SA_NOCLDWAIT;
504		sigemptyset(&sa.sa_mask);
505		(void)sigaction(SIGCHLD, &sa, NULL);
506
507		(void)memset(&hints, 0, sizeof(hints));
508		hints.ai_flags = AI_PASSIVE;
509		hints.ai_family = af;
510		hints.ai_socktype = SOCK_STREAM;
511		error = getaddrinfo(NULL, "ftp", &hints, &res0);
512		if (error) {
513			syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error));
514			exit(1);
515		}
516
517		for (n = 0, res = res0; res != NULL; res = res->ai_next)
518			n++;
519		if (n == 0) {
520			syslog(LOG_ERR, "no addresses available");
521			exit(1);
522		}
523		socks = malloc(n * sizeof(int));
524		fds = malloc(n * sizeof(struct pollfd));
525		if (socks == NULL || fds == NULL) {
526			syslog(LOG_ERR, "malloc: %m");
527			exit(1);
528		}
529
530		for (n = 0, res = res0; res != NULL; res = res->ai_next) {
531			socks[n] = socket(res->ai_family, res->ai_socktype,
532			    res->ai_protocol);
533			if (socks[n] == -1)
534				continue;
535			(void)setsockopt(socks[n], SOL_SOCKET, SO_REUSEADDR,
536			    &on, sizeof(on));
537			if (bind(socks[n], res->ai_addr, res->ai_addrlen)
538			    == -1) {
539				(void)close(socks[n]);
540				continue;
541			}
542			if (listen(socks[n], 12) == -1) {
543				(void)close(socks[n]);
544				continue;
545			}
546
547			fds[n].fd = socks[n];
548			fds[n].events = POLLIN;
549			n++;
550		}
551		if (n == 0) {
552			syslog(LOG_ERR, "%m");
553			exit(1);
554		}
555		freeaddrinfo(res0);
556
557		if (pidfile(NULL) == -1)
558			syslog(LOG_ERR, "failed to write a pid file: %m");
559
560		for (;;) {
561			if (poll(fds, n, INFTIM) == -1) {
562				if (errno == EINTR)
563					continue;
564				syslog(LOG_ERR, "poll: %m");
565				exit(1);
566			}
567			for (i = 0; i < n; i++) {
568				if (fds[i].revents & POLLIN) {
569					fd = accept(fds[i].fd, NULL, NULL);
570					if (fd == -1) {
571						syslog(LOG_ERR, "accept: %m");
572						continue;
573					}
574					switch (fork()) {
575					case -1:
576						syslog(LOG_ERR, "fork: %m");
577						break;
578					case 0:
579						goto child;
580						/* NOTREACHED */
581					}
582					(void)close(fd);
583				}
584			}
585		}
586 child:
587		(void)dup2(fd, STDIN_FILENO);
588		(void)dup2(fd, STDOUT_FILENO);
589		(void)dup2(fd, STDERR_FILENO);
590		for (i = 0; i < n; i++)
591			(void)close(socks[i]);
592	}
593
594	memset((char *)&his_addr, 0, sizeof(his_addr));
595	addrlen = sizeof(his_addr.si_su);
596	if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
597		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
598		exit(1);
599	}
600	his_addr.su_len = addrlen;
601	memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr));
602	addrlen = sizeof(ctrl_addr.si_su);
603	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
604		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
605		exit(1);
606	}
607	ctrl_addr.su_len = addrlen;
608#ifdef INET6
609	if (his_addr.su_family == AF_INET6
610	 && IN6_IS_ADDR_V4MAPPED(&his_addr.su_6addr)) {
611#if 1
612		/*
613		 * IPv4 control connection arrived to AF_INET6 socket.
614		 * I hate to do this, but this is the easiest solution.
615		 *
616		 * The assumption is untrue on SIIT environment.
617		 */
618		struct sockinet tmp_addr;
619		const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
620
621		tmp_addr = his_addr;
622		memset(&his_addr, 0, sizeof(his_addr));
623		his_addr.su_family = AF_INET;
624		his_addr.su_len = sizeof(his_addr.si_su.su_sin);
625		memcpy(&his_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
626		    sizeof(his_addr.su_addr));
627		his_addr.su_port = tmp_addr.su_port;
628
629		tmp_addr = ctrl_addr;
630		memset(&ctrl_addr, 0, sizeof(ctrl_addr));
631		ctrl_addr.su_family = AF_INET;
632		ctrl_addr.su_len = sizeof(ctrl_addr.si_su.su_sin);
633		memcpy(&ctrl_addr.su_addr, &tmp_addr.su_6addr.s6_addr[off],
634		    sizeof(ctrl_addr.su_addr));
635		ctrl_addr.su_port = tmp_addr.su_port;
636#else
637		while (fgets(line, sizeof(line), fd) != NULL) {
638			if ((cp = strchr(line, '\n')) != NULL)
639				*cp = '\0';
640			reply(-530, "%s", line);
641		}
642		(void) fflush(stdout);
643		(void) fclose(fd);
644		reply(530,
645		    "Connection from IPv4 mapped address is not supported.");
646		exit(0);
647#endif
648
649		mapped = 1;
650	} else
651#endif /* INET6 */
652		mapped = 0;
653#ifdef IP_TOS
654	if (!mapped && his_addr.su_family == AF_INET) {
655		tos = IPTOS_LOWDELAY;
656		if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos,
657			       sizeof(int)) < 0)
658			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
659	}
660#endif
661	/* if the hostname hasn't been given, attempt to determine it */
662	if (hostname[0] == '\0') {
663		if (getnameinfo((struct sockaddr *)&ctrl_addr.si_su,
664		    ctrl_addr.su_len, hostname, sizeof(hostname), NULL, 0, 0)
665		    != 0)
666			(void)gethostname(hostname, sizeof(hostname));
667		hostname[sizeof(hostname) - 1] = '\0';
668	}
669
670	/* set this here so klogin can use it... */
671	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
672
673	(void) freopen(_PATH_DEVNULL, "w", stderr);
674
675	memset(&sa, 0, sizeof(sa));
676	sa.sa_handler = SIG_DFL;
677	sa.sa_flags = SA_RESTART;
678	sigemptyset(&sa.sa_mask);
679	(void) sigaction(SIGCHLD, &sa, NULL);
680
681	sa.sa_handler = sigquit;
682	sa.sa_flags = SA_RESTART;
683	sigfillset(&sa.sa_mask);	/* block all sigs in these handlers */
684	(void) sigaction(SIGHUP, &sa, NULL);
685	(void) sigaction(SIGINT, &sa, NULL);
686	(void) sigaction(SIGQUIT, &sa, NULL);
687	(void) sigaction(SIGTERM, &sa, NULL);
688	sa.sa_handler = lostconn;
689	(void) sigaction(SIGPIPE, &sa, NULL);
690	sa.sa_handler = toolong;
691	(void) sigaction(SIGALRM, &sa, NULL);
692	sa.sa_handler = sigurg;
693	(void) sigaction(SIGURG, &sa, NULL);
694
695	/* Try to handle urgent data inline */
696#ifdef SO_OOBINLINE
697	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
698		syslog(LOG_WARNING, "setsockopt: %m");
699#endif
700	/* Set keepalives on the socket to detect dropped connections.  */
701#ifdef SO_KEEPALIVE
702	keepalive = 1;
703	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive,
704	    sizeof(int)) < 0)
705		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
706#endif
707
708#ifdef	F_SETOWN
709	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
710		syslog(LOG_WARNING, "fcntl F_SETOWN: %m");
711#endif
712	logremotehost(&his_addr);
713	/*
714	 * Set up default state
715	 */
716	data = -1;
717	type = TYPE_A;
718	form = FORM_N;
719	stru = STRU_F;
720	mode = MODE_S;
721	tmpline[0] = '\0';
722	hasyyerrored = 0;
723
724#ifdef KERBEROS5
725	kerror = krb5_init_context(&kcontext);
726	if (kerror) {
727		syslog(LOG_ERR, "%s when initializing Kerberos context",
728		    error_message(kerror));
729		exit(0);
730	}
731#endif /* KERBEROS5 */
732
733	init_curclass();
734	curclass.timeout = 300;		/* 5 minutes, as per login(1) */
735	curclass.type = CLASS_REAL;
736
737	/* If logins are disabled, print out the message. */
738	if (display_file(_PATH_NOLOGIN, 530)) {
739		reply(530, "System not available.");
740		exit(0);
741	}
742	(void)display_file(conffilename(_NAME_FTPWELCOME), 220);
743		/* reply(220,) must follow */
744	if (EMPTYSTR(version))
745		reply(220, "%s FTP server ready.", hostname);
746	else
747		reply(220, "%s FTP server (%s) ready.", hostname, version);
748
749	if (xferlogname != NULL) {
750		xferlogfd = open(xferlogname, O_WRONLY | O_APPEND | O_CREAT,
751		    0660);
752		if (xferlogfd == -1)
753			syslog(LOG_WARNING, "open xferlog `%s': %m",
754			    xferlogname);
755		else
756			doxferlog |= 2;
757	}
758
759	ftp_loop();
760	/* NOTREACHED */
761}
762
763static void
764lostconn(int signo)
765{
766
767	if (ftpd_debug)
768		syslog(LOG_DEBUG, "lost connection");
769	dologout(1);
770}
771
772static void
773toolong(int signo)
774{
775
776		/* XXXSIGRACE */
777	reply(421,
778	    "Timeout (" LLF " seconds): closing control connection.",
779	    (LLT)curclass.timeout);
780	if (logging)
781		syslog(LOG_INFO, "User %s timed out after " LLF " seconds",
782		    (pw ? pw->pw_name : "unknown"), (LLT)curclass.timeout);
783	dologout(1);
784}
785
786static void
787sigquit(int signo)
788{
789
790	if (ftpd_debug)
791		syslog(LOG_DEBUG, "got signal %d", signo);
792	dologout(1);
793}
794
795static void
796sigurg(int signo)
797{
798
799	urgflag = 1;
800}
801
802
803/*
804 * Save the result of a getpwnam.  Used for USER command, since
805 * the data returned must not be clobbered by any other command
806 * (e.g., globbing).
807 */
808static struct passwd *
809sgetpwnam(const char *name)
810{
811	static struct passwd save;
812	struct passwd *p;
813
814	if ((p = getpwnam(name)) == NULL)
815		return (p);
816	if (save.pw_name) {
817		free((char *)save.pw_name);
818		memset(save.pw_passwd, 0, strlen(save.pw_passwd));
819		free((char *)save.pw_passwd);
820		free((char *)save.pw_gecos);
821		free((char *)save.pw_dir);
822		free((char *)save.pw_shell);
823	}
824	save = *p;
825	save.pw_name = ftpd_strdup(p->pw_name);
826	save.pw_passwd = ftpd_strdup(p->pw_passwd);
827	save.pw_gecos = ftpd_strdup(p->pw_gecos);
828	save.pw_dir = ftpd_strdup(p->pw_dir);
829	save.pw_shell = ftpd_strdup(p->pw_shell);
830	return (&save);
831}
832
833static int	login_attempts;	/* number of failed login attempts */
834static int	askpasswd;	/* had USER command, ask for PASSwd */
835static int	permitted;	/* USER permitted */
836
837/*
838 * USER command.
839 * Sets global passwd pointer pw if named account exists and is acceptable;
840 * sets askpasswd if a PASS command is expected.  If logged in previously,
841 * need to reset state.  If name is "ftp" or "anonymous", the name is not in
842 * _NAME_FTPUSERS, and ftp account exists, set guest and pw, then just return.
843 * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
844 * requesting login privileges.  Disallow anyone who does not have a standard
845 * shell as returned by getusershell().  Disallow anyone mentioned in the file
846 * _NAME_FTPUSERS to allow people such as root and uucp to be avoided.
847 */
848void
849user(const char *name)
850{
851	char	*class;
852#ifdef	LOGIN_CAP
853	login_cap_t *lc = NULL;
854#endif
855
856	class = NULL;
857	if (logged_in) {
858		switch (curclass.type) {
859		case CLASS_GUEST:
860			reply(530, "Can't change user from guest login.");
861			return;
862		case CLASS_CHROOT:
863			reply(530, "Can't change user from chroot user.");
864			return;
865		case CLASS_REAL:
866			if (dropprivs) {
867				reply(530, "Can't change user.");
868				return;
869			}
870			end_login();
871			break;
872		default:
873			abort();
874		}
875	}
876
877#if defined(KERBEROS)
878	kdestroy();
879#endif
880#if defined(KERBEROS5)
881	k5destroy();
882#endif
883
884	curclass.type = CLASS_REAL;
885	askpasswd = 0;
886	permitted = 0;
887
888	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
889			/* need `pw' setup for checkaccess() and checkuser () */
890		if ((pw = sgetpwnam("ftp")) == NULL)
891			reply(530, "User %s unknown.", name);
892		else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
893			reply(530, "User %s access denied.", name);
894		else {
895			curclass.type = CLASS_GUEST;
896			askpasswd = 1;
897			reply(331,
898			    "Guest login ok, type your name as password.");
899		}
900		if (!askpasswd) {
901			if (logging)
902				syslog(LOG_NOTICE,
903				    "ANONYMOUS FTP LOGIN REFUSED FROM %s",
904				    remotehost);
905			end_login();
906			goto cleanup_user;
907		}
908		name = "ftp";
909	} else
910		pw = sgetpwnam(name);
911
912	strlcpy(curname, name, curname_len);
913
914			/* check user in /etc/ftpusers, and setup class */
915	permitted = checkuser(_NAME_FTPUSERS, curname, 1, 0, &class);
916
917			/* check user in /etc/ftpchroot */
918	lc = login_getpwclass(pw);
919	if (checkuser(_NAME_FTPCHROOT, curname, 0, 0, NULL)
920#ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
921	    || login_getcapbool(lc, "ftp-chroot", 0)
922#endif
923	) {
924		if (curclass.type == CLASS_GUEST) {
925			syslog(LOG_NOTICE,
926	    "Can't change guest user to chroot class; remove entry in %s",
927			    _NAME_FTPCHROOT);
928			exit(1);
929		}
930		curclass.type = CLASS_CHROOT;
931	}
932			/* determine default class */
933	if (class == NULL) {
934		switch (curclass.type) {
935		case CLASS_GUEST:
936			class = ftpd_strdup("guest");
937			break;
938		case CLASS_CHROOT:
939			class = ftpd_strdup("chroot");
940			break;
941		case CLASS_REAL:
942			class = ftpd_strdup("real");
943			break;
944		default:
945			syslog(LOG_ERR, "unknown curclass.type %d; aborting",
946			    curclass.type);
947			abort();
948		}
949	}
950			/* parse ftpd.conf, setting up various parameters */
951	parse_conf(class);
952			/* if not guest user, check for valid shell */
953	if (pw == NULL)
954		permitted = 0;
955	else {
956		const char	*cp, *shell;
957
958		if ((shell = pw->pw_shell) == NULL || *shell == 0)
959			shell = _PATH_BSHELL;
960		while ((cp = getusershell()) != NULL)
961			if (strcmp(cp, shell) == 0)
962				break;
963		endusershell();
964		if (cp == NULL && curclass.type != CLASS_GUEST)
965			permitted = 0;
966	}
967
968			/* deny quickly (after USER not PASS) if requested */
969	if (CURCLASS_FLAGS_ISSET(denyquick) && !permitted) {
970		reply(530, "User %s may not use FTP.", curname);
971		if (logging)
972			syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
973			    remotehost, curname);
974		end_login();
975		goto cleanup_user;
976	}
977
978			/* if haven't asked yet (i.e, not anon), ask now */
979	if (!askpasswd) {
980		askpasswd = 1;
981#ifdef SKEY
982		if (skey_haskey(curname) == 0) {
983			const char *myskey;
984
985			myskey = skey_keyinfo(curname);
986			reply(331, "Password [ %s ] required for %s.",
987			    myskey ? myskey : "error getting challenge",
988			    curname);
989		} else
990#endif
991#ifdef USE_OPIE
992		if (opiechallenge(&opiedata, (char *)curname, opieprompt) ==
993		    0) {
994			pwok = (pw != NULL) &&
995			    opieaccessfile(remotehost) &&
996			    opiealways(pw->pw_dir);
997			reply(331, "Response to %s %s for %s.",
998			    opieprompt, pwok ? "requested" : "required",
999			    curname);
1000		} else
1001#endif
1002			reply(331, "Password required for %s.", curname);
1003	}
1004
1005 cleanup_user:
1006#ifdef LOGIN_CAP
1007	login_close(lc);
1008#endif
1009	/*
1010	 * Delay before reading passwd after first failed
1011	 * attempt to slow down passwd-guessing programs.
1012	 */
1013	if (login_attempts)
1014		sleep((unsigned) login_attempts);
1015
1016	if (class)
1017		free(class);
1018}
1019
1020/*
1021 * Determine whether something is to happen (allow access, chroot)
1022 * for a user. Each line is a shell-style glob followed by
1023 * `yes' or `no'.
1024 *
1025 * For backward compatibility, `allow' and `deny' are synonymns
1026 * for `yes' and `no', respectively.
1027 *
1028 * Each glob is matched against the username in turn, and the first
1029 * match found is used. If no match is found, the result is the
1030 * argument `def'. If a match is found but without and explicit
1031 * `yes'/`no', the result is the opposite of def.
1032 *
1033 * If the file doesn't exist at all, the result is the argument
1034 * `nofile'
1035 *
1036 * Any line starting with `#' is considered a comment and ignored.
1037 *
1038 * Returns 0 if the user is denied, or 1 if they are allowed.
1039 *
1040 * NOTE: needs struct passwd *pw setup before use.
1041 */
1042static int
1043checkuser(const char *fname, const char *name, int def, int nofile,
1044	    char **retclass)
1045{
1046	FILE	*fd;
1047	int	 retval;
1048	char	*word, *perm, *class, *buf, *p;
1049	size_t	 len, line;
1050
1051	retval = def;
1052	if (retclass != NULL)
1053		*retclass = NULL;
1054	if ((fd = fopen(conffilename(fname), "r")) == NULL)
1055		return nofile;
1056
1057	line = 0;
1058	for (;
1059	    (buf = fparseln(fd, &len, &line, NULL, FPARSELN_UNESCCOMM |
1060			    FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL;
1061	    free(buf), buf = NULL) {
1062		word = perm = class = NULL;
1063		p = buf;
1064		if (len < 1)
1065			continue;
1066		if (p[len - 1] == '\n')
1067			p[--len] = '\0';
1068		if (EMPTYSTR(p))
1069			continue;
1070
1071		NEXTWORD(p, word);
1072		NEXTWORD(p, perm);
1073		NEXTWORD(p, class);
1074		if (EMPTYSTR(word))
1075			continue;
1076		if (!EMPTYSTR(class)) {
1077			if (strcasecmp(class, "all") == 0 ||
1078			    strcasecmp(class, "none") == 0) {
1079				syslog(LOG_WARNING,
1080		"%s line %d: illegal user-defined class `%s' - skipping entry",
1081					    fname, (int)line, class);
1082				continue;
1083			}
1084		}
1085
1086					/* have a host specifier */
1087		if ((p = strchr(word, '@')) != NULL) {
1088			unsigned long	net, mask, addr;
1089			int		bits;
1090
1091			*p++ = '\0';
1092					/* check against network or CIDR */
1093			if (isdigit((unsigned char)*p) &&
1094			    (bits = inet_net_pton(AF_INET, p,
1095			    &net, sizeof(net))) != -1) {
1096				net = ntohl(net);
1097				mask = 0xffffffffU << (32 - bits);
1098				addr = ntohl(his_addr.su_addr.s_addr);
1099				if ((addr & mask) != net)
1100					continue;
1101
1102					/* check against hostname glob */
1103			} else if (fnmatch(p, remotehost, FNM_CASEFOLD) != 0)
1104				continue;
1105		}
1106
1107					/* have a group specifier */
1108		if ((p = strchr(word, ':')) != NULL) {
1109			gid_t	*groups, *ng;
1110			int	 gsize, i, found;
1111
1112			if (pw == NULL)
1113				continue;	/* no match for unknown user */
1114			*p++ = '\0';
1115			groups = NULL;
1116			gsize = 16;
1117			do {
1118				ng = realloc(groups, gsize * sizeof(gid_t));
1119				if (ng == NULL)
1120					fatal(
1121					    "Local resource failure: realloc");
1122				groups = ng;
1123			} while (getgrouplist(pw->pw_name, pw->pw_gid,
1124						groups, &gsize) == -1);
1125			found = 0;
1126			for (i = 0; i < gsize; i++) {
1127				struct group *g;
1128
1129				if ((g = getgrgid(groups[i])) == NULL)
1130					continue;
1131				if (fnmatch(p, g->gr_name, 0) == 0) {
1132					found = 1;
1133					break;
1134				}
1135			}
1136			free(groups);
1137			if (!found)
1138				continue;
1139		}
1140
1141					/* check against username glob */
1142		if (fnmatch(word, name, 0) != 0)
1143			continue;
1144
1145		if (perm != NULL &&
1146		    ((strcasecmp(perm, "allow") == 0) ||
1147		     (strcasecmp(perm, "yes") == 0)))
1148			retval = 1;
1149		else if (perm != NULL &&
1150		    ((strcasecmp(perm, "deny") == 0) ||
1151		     (strcasecmp(perm, "no") == 0)))
1152			retval = 0;
1153		else
1154			retval = !def;
1155		if (!EMPTYSTR(class) && retclass != NULL)
1156			*retclass = ftpd_strdup(class);
1157		free(buf);
1158		break;
1159	}
1160	(void) fclose(fd);
1161	return (retval);
1162}
1163
1164/*
1165 * Check if user is allowed by /etc/ftpusers
1166 * returns 1 for yes, 0 for no
1167 *
1168 * NOTE: needs struct passwd *pw setup (for checkuser())
1169 */
1170static int
1171checkaccess(const char *name)
1172{
1173
1174	return (checkuser(_NAME_FTPUSERS, name, 1, 0, NULL));
1175}
1176
1177static void
1178login_utmp(const char *line, const char *name, const char *host,
1179    struct sockinet *haddr)
1180{
1181#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
1182	struct timeval tv;
1183	(void)gettimeofday(&tv, NULL);
1184#endif
1185#ifdef SUPPORT_UTMPX
1186	if (doutmp) {
1187		(void)memset(&utmpx, 0, sizeof(utmpx));
1188		utmpx.ut_tv = tv;
1189		utmpx.ut_pid = getpid();
1190		utmpx.ut_id[0] = 'f';
1191		utmpx.ut_id[1] = 't';
1192		utmpx.ut_id[2] = 'p';
1193		utmpx.ut_id[3] = '*';
1194		utmpx.ut_type = USER_PROCESS;
1195		(void)strncpy(utmpx.ut_name, name, sizeof(utmpx.ut_name));
1196		(void)strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
1197		(void)strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
1198		(void)memcpy(&utmpx.ut_ss, &haddr->si_su, haddr->su_len);
1199		ftpd_loginx(&utmpx);
1200	}
1201	if (dowtmp)
1202		ftpd_logwtmpx(line, name, host, haddr, 0, USER_PROCESS);
1203#endif
1204#ifdef SUPPORT_UTMP
1205	if (doutmp) {
1206		(void)memset(&utmp, 0, sizeof(utmp));
1207		(void)time(&utmp.ut_time);
1208		(void)strncpy(utmp.ut_name, name, sizeof(utmp.ut_name));
1209		(void)strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
1210		(void)strncpy(utmp.ut_host, host, sizeof(utmp.ut_host));
1211		ftpd_login(&utmp);
1212	}
1213	if (dowtmp)
1214		ftpd_logwtmp(line, name, host);
1215#endif
1216}
1217
1218static void
1219logout_utmp(void)
1220{
1221#ifdef SUPPORT_UTMPX
1222	int okwtmpx = dowtmp;
1223#endif
1224#ifdef SUPPORT_UTMP
1225	int okwtmp = dowtmp;
1226#endif
1227	if (logged_in) {
1228#ifdef SUPPORT_UTMPX
1229		if (doutmp)
1230			okwtmpx &= ftpd_logoutx(ttyline, 0, DEAD_PROCESS);
1231		if (okwtmpx)
1232			ftpd_logwtmpx(ttyline, "", "", NULL, 0, DEAD_PROCESS);
1233#endif
1234#ifdef SUPPORT_UTMP
1235		if (doutmp)
1236			okwtmp &= ftpd_logout(ttyline);
1237		if (okwtmp)
1238			ftpd_logwtmp(ttyline, "", "");
1239#endif
1240	}
1241}
1242
1243/*
1244 * Terminate login as previous user (if any), resetting state;
1245 * used when USER command is given or login fails.
1246 */
1247static void
1248end_login(void)
1249{
1250#ifdef USE_PAM
1251	int e;
1252#endif
1253	logout_utmp();
1254	show_chdir_messages(-1);		/* flush chdir cache */
1255	if (pw != NULL && pw->pw_passwd != NULL)
1256		memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
1257	pw = NULL;
1258	logged_in = 0;
1259	askpasswd = 0;
1260	permitted = 0;
1261	quietmessages = 0;
1262	gidcount = 0;
1263	curclass.type = CLASS_REAL;
1264	(void) seteuid((uid_t)0);
1265#ifdef	LOGIN_CAP
1266	setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN |
1267		       LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH |
1268		       LOGIN_SETENV));
1269#endif
1270#ifdef USE_PAM
1271	if (pamh) {
1272		if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
1273			syslog(LOG_ERR, "pam_setcred: %s",
1274			    pam_strerror(pamh, e));
1275		if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
1276			syslog(LOG_ERR, "pam_close_session: %s",
1277			    pam_strerror(pamh, e));
1278		if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
1279			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1280		pamh = NULL;
1281	}
1282#endif
1283}
1284
1285void
1286pass(const char *passwd)
1287{
1288	int		 rval;
1289	char		 root[MAXPATHLEN];
1290#ifdef	LOGIN_CAP
1291	login_cap_t *lc = NULL;
1292#endif
1293#ifdef USE_PAM
1294	int e;
1295#endif
1296	if (logged_in || askpasswd == 0) {
1297		reply(503, "Login with USER first.");
1298		return;
1299	}
1300	askpasswd = 0;
1301	if (curclass.type != CLASS_GUEST) {
1302			/* "ftp" is the only account allowed with no password */
1303		if (pw == NULL) {
1304			rval = 1;	/* failure below */
1305			goto skip;
1306		}
1307#ifdef USE_PAM
1308		rval = auth_pam(&pw, passwd);
1309#ifdef notdef
1310		/* If PAM fails, we proceed with other authentications */
1311		if (rval >= 0) {
1312#ifdef USE_OPIE
1313			opieunlock();
1314#endif
1315			goto skip;
1316		}
1317#else
1318		/* If PAM fails, that's it */
1319		goto skip;
1320#endif
1321#endif
1322#if defined(KERBEROS)
1323		if (klogin(pw, "", hostname, (char *)passwd) == 0) {
1324			rval = 0;
1325			goto skip;
1326		}
1327#endif
1328#if defined(KERBEROS5)
1329		if (k5login(pw, "", hostname, (char *)passwd) == 0) {
1330			rval = 0;
1331			goto skip;
1332		}
1333#endif
1334#ifdef SKEY
1335		if (skey_haskey(pw->pw_name) == 0) {
1336			char *p;
1337			int r;
1338
1339			p = ftpd_strdup(passwd);
1340			r = skey_passcheck(pw->pw_name, p);
1341			free(p);
1342			if (r != -1) {
1343				rval = 0;
1344				goto skip;
1345			}
1346		}
1347#endif
1348#ifdef USE_OPIE
1349		if (opieverify(&opiedata, (char *)passwd) == 0) {
1350			/* OPIE says ok, check expire time */
1351			if (pw->pw_expire && time(NULL) >= pw->pw_expire)
1352				rval = 2;
1353			else
1354				rval = 0;
1355			goto skip;
1356		}
1357#endif
1358		if (!sflag)
1359			rval = checkpassword(pw, passwd);
1360		else
1361			rval = 1;
1362
1363 skip:
1364
1365			/*
1366			 * If rval > 0, the user failed the authentication check
1367			 * above.  If rval == 0, either Kerberos or local
1368			 * authentication succeeded.
1369			 */
1370		if (rval) {
1371			reply(530, "%s", rval == 2 ? "Password expired." :
1372			    "Login incorrect.");
1373			if (logging) {
1374				syslog(LOG_NOTICE,
1375				    "FTP LOGIN FAILED FROM %s", remotehost);
1376				syslog(LOG_AUTHPRIV | LOG_NOTICE,
1377				    "FTP LOGIN FAILED FROM %s, %s",
1378				    remotehost, curname);
1379			}
1380			pw = NULL;
1381			if (login_attempts++ >= 5) {
1382				syslog(LOG_NOTICE,
1383				    "repeated login failures from %s",
1384				    remotehost);
1385				exit(0);
1386			}
1387			return;
1388		}
1389	}
1390
1391			/* password ok; check if anything else prevents login */
1392	if (! permitted) {
1393		reply(530, "User %s may not use FTP.", pw->pw_name);
1394		if (logging)
1395			syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
1396			    remotehost, pw->pw_name);
1397		goto bad;
1398	}
1399
1400	login_attempts = 0;		/* this time successful */
1401	if (setegid((gid_t)pw->pw_gid) < 0) {
1402		reply(550, "Can't set gid.");
1403		goto bad;
1404	}
1405#ifdef	LOGIN_CAP
1406	if ((lc = login_getpwclass(pw)) != NULL) {
1407#ifdef notyet
1408		char	remote_ip[NI_MAXHOST];
1409
1410		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1411			remote_ip, sizeof(remote_ip) - 1, NULL, 0,
1412			NI_NUMERICHOST))
1413				*remote_ip = 0;
1414		remote_ip[sizeof(remote_ip) - 1] = 0;
1415		if (!auth_hostok(lc, remotehost, remote_ip)) {
1416			syslog(LOG_INFO|LOG_AUTH,
1417			    "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1418			    pw->pw_name);
1419			reply(530, "Permission denied.");
1420			pw = NULL;
1421			return;
1422		}
1423		if (!auth_timeok(lc, time(NULL))) {
1424			reply(530, "Login not available right now.");
1425			pw = NULL;
1426			return;
1427		}
1428#endif
1429	}
1430	setsid();
1431	setusercontext(lc, pw, 0, LOGIN_SETALL &
1432		       ~(LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV));
1433#else
1434	(void) initgroups(pw->pw_name, pw->pw_gid);
1435			/* cache groups for cmds.c::matchgroup() */
1436#endif
1437#ifdef USE_PAM
1438	if (pamh) {
1439		if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
1440			syslog(LOG_ERR, "pam_open_session: %s",
1441			    pam_strerror(pamh, e));
1442		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
1443		    != PAM_SUCCESS) {
1444			syslog(LOG_ERR, "pam_setcred: %s",
1445			    pam_strerror(pamh, e));
1446		}
1447	}
1448#endif
1449	gidcount = getgroups(0, NULL);
1450	if (gidlist)
1451		free(gidlist);
1452	gidlist = malloc(gidcount * sizeof *gidlist);
1453	gidcount = getgroups(gidcount, gidlist);
1454
1455	/* open utmp/wtmp before chroot */
1456	login_utmp(ttyline, pw->pw_name, remotehost, &his_addr);
1457
1458	logged_in = 1;
1459
1460	connections = 1;
1461	if (dopidfile)
1462		count_users();
1463	if (curclass.limit != -1 && connections > curclass.limit) {
1464		if (! EMPTYSTR(curclass.limitfile))
1465			(void)display_file(conffilename(curclass.limitfile),
1466			    530);
1467		reply(530,
1468		    "User %s access denied, connection limit of " LLF
1469		    " reached.",
1470		    pw->pw_name, (LLT)curclass.limit);
1471		syslog(LOG_NOTICE,
1472		    "Maximum connection limit of " LLF
1473		    " for class %s reached, login refused for %s",
1474		    (LLT)curclass.limit, curclass.classname, pw->pw_name);
1475		goto bad;
1476	}
1477
1478	homedir[0] = '/';
1479	switch (curclass.type) {
1480	case CLASS_GUEST:
1481			/*
1482			 * We MUST do a chdir() after the chroot. Otherwise
1483			 * the old current directory will be accessible as "."
1484			 * outside the new root!
1485			 */
1486		format_path(root,
1487		    curclass.chroot ? curclass.chroot :
1488		    anondir ? anondir :
1489		    pw->pw_dir);
1490		format_path(homedir,
1491		    curclass.homedir ? curclass.homedir :
1492		    "/");
1493		if (EMPTYSTR(homedir))
1494			homedir[0] = '/';
1495		if (EMPTYSTR(root) || chroot(root) < 0) {
1496			syslog(LOG_NOTICE,
1497			    "GUEST user %s: can't chroot to %s: %m",
1498			    pw->pw_name, root);
1499			goto bad_guest;
1500		}
1501		if (chdir(homedir) < 0) {
1502			syslog(LOG_NOTICE,
1503			    "GUEST user %s: can't chdir to %s: %m",
1504			    pw->pw_name, homedir);
1505 bad_guest:
1506			reply(550, "Can't set guest privileges.");
1507			goto bad;
1508		}
1509		break;
1510	case CLASS_CHROOT:
1511		format_path(root,
1512		    curclass.chroot ? curclass.chroot :
1513		    pw->pw_dir);
1514		format_path(homedir,
1515		    curclass.homedir ? curclass.homedir :
1516		    "/");
1517		if (EMPTYSTR(homedir))
1518			homedir[0] = '/';
1519		if (EMPTYSTR(root) || chroot(root) < 0) {
1520			syslog(LOG_NOTICE,
1521			    "CHROOT user %s: can't chroot to %s: %m",
1522			    pw->pw_name, root);
1523			goto bad_chroot;
1524		}
1525		if (chdir(homedir) < 0) {
1526			syslog(LOG_NOTICE,
1527			    "CHROOT user %s: can't chdir to %s: %m",
1528			    pw->pw_name, homedir);
1529 bad_chroot:
1530			reply(550, "Can't change root.");
1531			goto bad;
1532		}
1533		break;
1534	case CLASS_REAL:
1535			/* only chroot REAL if explicitly requested */
1536		if (! EMPTYSTR(curclass.chroot)) {
1537			format_path(root, curclass.chroot);
1538			if (EMPTYSTR(root) || chroot(root) < 0) {
1539				syslog(LOG_NOTICE,
1540				    "REAL user %s: can't chroot to %s: %m",
1541				    pw->pw_name, root);
1542				goto bad_chroot;
1543			}
1544		}
1545		format_path(homedir,
1546		    curclass.homedir ? curclass.homedir :
1547		    pw->pw_dir);
1548		if (EMPTYSTR(homedir) || chdir(homedir) < 0) {
1549			if (chdir("/") < 0) {
1550				syslog(LOG_NOTICE,
1551				    "REAL user %s: can't chdir to %s: %m",
1552				    pw->pw_name,
1553				    !EMPTYSTR(homedir) ?  homedir : "/");
1554				reply(530,
1555				    "User %s: can't change directory to %s.",
1556				    pw->pw_name,
1557				    !EMPTYSTR(homedir) ? homedir : "/");
1558				goto bad;
1559			} else {
1560				reply(-230,
1561				    "No directory! Logging in with home=/");
1562				homedir[0] = '/';
1563			}
1564		}
1565		break;
1566	}
1567#ifndef LOGIN_CAP
1568	setsid();
1569	setlogin(pw->pw_name);
1570#endif
1571	if (dropprivs ||
1572	    (curclass.type != CLASS_REAL &&
1573	    ntohs(ctrl_addr.su_port) > IPPORT_RESERVED + 1)) {
1574		dropprivs++;
1575		if (setgid((gid_t)pw->pw_gid) < 0) {
1576			reply(550, "Can't set gid.");
1577			goto bad;
1578		}
1579		if (setuid((uid_t)pw->pw_uid) < 0) {
1580			reply(550, "Can't set uid.");
1581			goto bad;
1582		}
1583	} else {
1584		if (seteuid((uid_t)pw->pw_uid) < 0) {
1585			reply(550, "Can't set uid.");
1586			goto bad;
1587		}
1588	}
1589	setenv("HOME", homedir, 1);
1590
1591	if (curclass.type == CLASS_GUEST && passwd[0] == '-')
1592		quietmessages = 1;
1593
1594			/*
1595			 * Display a login message, if it exists.
1596			 * N.B. reply(230,) must follow the message.
1597			 */
1598	if (! EMPTYSTR(curclass.motd))
1599		(void)display_file(conffilename(curclass.motd), 230);
1600	show_chdir_messages(230);
1601	if (curclass.type == CLASS_GUEST) {
1602		char *p;
1603
1604		reply(230, "Guest login ok, access restrictions apply.");
1605#if HAVE_SETPROCTITLE
1606		snprintf(proctitle, sizeof(proctitle),
1607		    "%s: anonymous/%s", remotehost, passwd);
1608		setproctitle("%s", proctitle);
1609#endif /* HAVE_SETPROCTITLE */
1610		if (logging)
1611			syslog(LOG_INFO,
1612			"ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)",
1613			    remotehost, passwd,
1614			    curclass.classname, CURCLASSTYPE);
1615			/* store guest password reply into pw_passwd */
1616		REASSIGN(pw->pw_passwd, ftpd_strdup(passwd));
1617		for (p = pw->pw_passwd; *p; p++)
1618			if (!isgraph((unsigned char)*p))
1619				*p = '_';
1620	} else {
1621		reply(230, "User %s logged in.", pw->pw_name);
1622#if HAVE_SETPROCTITLE
1623		snprintf(proctitle, sizeof(proctitle),
1624		    "%s: %s", remotehost, pw->pw_name);
1625		setproctitle("%s", proctitle);
1626#endif /* HAVE_SETPROCTITLE */
1627		if (logging)
1628			syslog(LOG_INFO,
1629			    "FTP LOGIN FROM %s as %s (class: %s, type: %s)",
1630			    remotehost, pw->pw_name,
1631			    curclass.classname, CURCLASSTYPE);
1632	}
1633	(void) umask(curclass.umask);
1634#ifdef	LOGIN_CAP
1635	login_close(lc);
1636#endif
1637	return;
1638
1639 bad:
1640#ifdef	LOGIN_CAP
1641	login_close(lc);
1642#endif
1643			/* Forget all about it... */
1644	end_login();
1645}
1646
1647void
1648retrieve(char *argv[], const char *name)
1649{
1650	FILE *fin, *dout;
1651	struct stat st;
1652	int (*closefunc)(FILE *) = NULL;
1653	int dolog, sendrv, closerv, stderrfd, isconversion, isdata, isls;
1654	struct timeval start, finish, td, *tdp;
1655	struct rusage rusage_before, rusage_after;
1656	const char *dispname;
1657	char *error;
1658
1659	sendrv = closerv = stderrfd = -1;
1660	isconversion = isdata = isls = dolog = 0;
1661	tdp = NULL;
1662	dispname = name;
1663	fin = dout = NULL;
1664	error = NULL;
1665	if (argv == NULL) {		/* if not running a command ... */
1666		dolog = 1;
1667		isdata = 1;
1668		fin = fopen(name, "r");
1669		closefunc = fclose;
1670		if (fin == NULL)	/* doesn't exist?; try a conversion */
1671			argv = do_conversion(name);
1672		if (argv != NULL) {
1673			isconversion++;
1674			syslog(LOG_DEBUG, "get command: '%s' on '%s'",
1675			    argv[0], name);
1676		}
1677	}
1678	if (argv != NULL) {
1679		char temp[MAXPATHLEN];
1680
1681		if (strcmp(argv[0], INTERNAL_LS) == 0) {
1682			isls = 1;
1683			stderrfd = -1;
1684		} else {
1685			(void)snprintf(temp, sizeof(temp), "%s", TMPFILE);
1686			stderrfd = mkstemp(temp);
1687			if (stderrfd != -1)
1688				(void)unlink(temp);
1689		}
1690		dispname = argv[0];
1691		fin = ftpd_popen(argv, "r", stderrfd);
1692		closefunc = ftpd_pclose;
1693		st.st_size = -1;
1694		st.st_blksize = BUFSIZ;
1695	}
1696	if (fin == NULL) {
1697		if (errno != 0) {
1698			perror_reply(550, dispname);
1699			if (dolog)
1700				logxfer("get", -1, name, NULL, NULL,
1701				    strerror(errno));
1702		}
1703		goto cleanupretrieve;
1704	}
1705	byte_count = -1;
1706	if (argv == NULL
1707	    && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
1708		error = "Not a plain file";
1709		reply(550, "%s: %s.", dispname, error);
1710		goto done;
1711	}
1712	if (restart_point) {
1713		if (type == TYPE_A) {
1714			off_t i;
1715			int c;
1716
1717			for (i = 0; i < restart_point; i++) {
1718				if ((c=getc(fin)) == EOF) {
1719					error = strerror(errno);
1720					perror_reply(550, dispname);
1721					goto done;
1722				}
1723				if (c == '\n')
1724					i++;
1725			}
1726		} else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
1727			error = strerror(errno);
1728			perror_reply(550, dispname);
1729			goto done;
1730		}
1731	}
1732	dout = dataconn(dispname, st.st_size, "w");
1733	if (dout == NULL)
1734		goto done;
1735
1736	(void)getrusage(RUSAGE_SELF, &rusage_before);
1737	(void)gettimeofday(&start, NULL);
1738	sendrv = send_data(fin, dout, &st, isdata);
1739	(void)gettimeofday(&finish, NULL);
1740	(void)getrusage(RUSAGE_SELF, &rusage_after);
1741	closedataconn(dout);		/* close now to affect timing stats */
1742	timersub(&finish, &start, &td);
1743	tdp = &td;
1744 done:
1745	if (dolog) {
1746		logxfer("get", byte_count, name, NULL, tdp, error);
1747		if (tdp != NULL)
1748			logrusage(&rusage_before, &rusage_after);
1749	}
1750	closerv = (*closefunc)(fin);
1751	if (sendrv == 0) {
1752		FILE *errf;
1753		struct stat sb;
1754
1755		if (!isls && argv != NULL && closerv != 0) {
1756			reply(-226,
1757			    "Command returned an exit status of %d",
1758			    closerv);
1759			if (isconversion)
1760				syslog(LOG_WARNING,
1761				    "retrieve command: '%s' returned %d",
1762				    argv[0], closerv);
1763		}
1764		if (!isls && argv != NULL && stderrfd != -1 &&
1765		    (fstat(stderrfd, &sb) == 0) && sb.st_size > 0 &&
1766		    ((errf = fdopen(stderrfd, "r")) != NULL)) {
1767			char *cp, line[LINE_MAX];
1768
1769			reply(-226, "Command error messages:");
1770			rewind(errf);
1771			while (fgets(line, sizeof(line), errf) != NULL) {
1772				if ((cp = strchr(line, '\n')) != NULL)
1773					*cp = '\0';
1774				reply(0, "  %s", line);
1775			}
1776			(void) fflush(stdout);
1777			(void) fclose(errf);
1778				/* a reply(226,) must follow */
1779		}
1780		reply(226, "Transfer complete.");
1781	}
1782 cleanupretrieve:
1783	if (stderrfd != -1)
1784		(void)close(stderrfd);
1785	if (isconversion)
1786		free(argv);
1787}
1788
1789void
1790store(const char *name, const char *fmode, int unique)
1791{
1792	FILE *fout, *din;
1793	struct stat st;
1794	int (*closefunc)(FILE *);
1795	struct timeval start, finish, td, *tdp;
1796	char *desc, *error;
1797
1798	din = NULL;
1799	desc = (*fmode == 'w') ? "put" : "append";
1800	error = NULL;
1801	if (unique && stat(name, &st) == 0 &&
1802	    (name = gunique(name)) == NULL) {
1803		logxfer(desc, -1, name, NULL, NULL,
1804		    "cannot create unique file");
1805		goto cleanupstore;
1806	}
1807
1808	if (restart_point)
1809		fmode = "r+";
1810	fout = fopen(name, fmode);
1811	closefunc = fclose;
1812	tdp = NULL;
1813	if (fout == NULL) {
1814		perror_reply(553, name);
1815		logxfer(desc, -1, name, NULL, NULL, strerror(errno));
1816		goto cleanupstore;
1817	}
1818	byte_count = -1;
1819	if (restart_point) {
1820		if (type == TYPE_A) {
1821			off_t i;
1822			int c;
1823
1824			for (i = 0; i < restart_point; i++) {
1825				if ((c=getc(fout)) == EOF) {
1826					error = strerror(errno);
1827					perror_reply(550, name);
1828					goto done;
1829				}
1830				if (c == '\n')
1831					i++;
1832			}
1833			/*
1834			 * We must do this seek to "current" position
1835			 * because we are changing from reading to
1836			 * writing.
1837			 */
1838			if (fseek(fout, 0L, SEEK_CUR) < 0) {
1839				error = strerror(errno);
1840				perror_reply(550, name);
1841				goto done;
1842			}
1843		} else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
1844			error = strerror(errno);
1845			perror_reply(550, name);
1846			goto done;
1847		}
1848	}
1849	din = dataconn(name, (off_t)-1, "r");
1850	if (din == NULL)
1851		goto done;
1852	(void)gettimeofday(&start, NULL);
1853	if (receive_data(din, fout) == 0) {
1854		if (unique)
1855			reply(226, "Transfer complete (unique file name:%s).",
1856			    name);
1857		else
1858			reply(226, "Transfer complete.");
1859	}
1860	(void)gettimeofday(&finish, NULL);
1861	closedataconn(din);		/* close now to affect timing stats */
1862	timersub(&finish, &start, &td);
1863	tdp = &td;
1864 done:
1865	logxfer(desc, byte_count, name, NULL, tdp, error);
1866	(*closefunc)(fout);
1867 cleanupstore:
1868	;
1869}
1870
1871static FILE *
1872getdatasock(const char *fmode)
1873{
1874	int		on, s, t, tries;
1875	in_port_t	port;
1876
1877	on = 1;
1878	if (data >= 0)
1879		return (fdopen(data, fmode));
1880	if (! dropprivs)
1881		(void) seteuid((uid_t)0);
1882	s = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
1883	if (s < 0)
1884		goto bad;
1885	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1886	    (char *) &on, sizeof(on)) < 0)
1887		goto bad;
1888	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1889	    (char *) &on, sizeof(on)) < 0)
1890		goto bad;
1891			/* anchor socket to avoid multi-homing problems */
1892	data_source = ctrl_addr;
1893			/*
1894			 * By default source port for PORT connctions is
1895			 * ctrlport-1 (see RFC959 section 5.2).
1896			 * However, if privs have been dropped and that
1897			 * would be < IPPORT_RESERVED, use a random port
1898			 * instead.
1899			 */
1900	if (dataport)
1901		port = dataport;
1902	else
1903		port = ntohs(ctrl_addr.su_port) - 1;
1904	if (dropprivs && port < IPPORT_RESERVED)
1905		port = 0;		/* use random port */
1906	data_source.su_port = htons(port);
1907
1908	for (tries = 1; ; tries++) {
1909		if (bind(s, (struct sockaddr *)&data_source.si_su,
1910		    data_source.su_len) >= 0)
1911			break;
1912		if (errno != EADDRINUSE || tries > 10)
1913			goto bad;
1914		sleep(tries);
1915	}
1916	if (! dropprivs)
1917		(void) seteuid((uid_t)pw->pw_uid);
1918#ifdef IP_TOS
1919	if (!mapped && ctrl_addr.su_family == AF_INET) {
1920		on = IPTOS_THROUGHPUT;
1921		if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on,
1922			       sizeof(int)) < 0)
1923			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1924	}
1925#endif
1926	return (fdopen(s, fmode));
1927 bad:
1928		/* Return the real value of errno (close may change it) */
1929	t = errno;
1930	if (! dropprivs)
1931		(void) seteuid((uid_t)pw->pw_uid);
1932	(void) close(s);
1933	errno = t;
1934	return (NULL);
1935}
1936
1937FILE *
1938dataconn(const char *name, off_t size, const char *fmode)
1939{
1940	char sizebuf[32];
1941	FILE *file;
1942	int retry, tos, keepalive, conerrno;
1943
1944	file_size = size;
1945	byte_count = 0;
1946	if (size != (off_t) -1)
1947		(void)snprintf(sizebuf, sizeof(sizebuf), " (" LLF " byte%s)",
1948		    (LLT)size, PLURAL(size));
1949	else
1950		sizebuf[0] = '\0';
1951	if (pdata >= 0) {
1952		struct sockinet from;
1953		int s;
1954		socklen_t fromlen = sizeof(from.su_len);
1955
1956		(void) alarm(curclass.timeout);
1957		s = accept(pdata, (struct sockaddr *)&from.si_su, &fromlen);
1958		(void) alarm(0);
1959		if (s < 0) {
1960			reply(425, "Can't open data connection.");
1961			(void) close(pdata);
1962			pdata = -1;
1963			return (NULL);
1964		}
1965		(void) close(pdata);
1966		pdata = s;
1967		switch (from.su_family) {
1968		case AF_INET:
1969#ifdef IP_TOS
1970			if (!mapped) {
1971				tos = IPTOS_THROUGHPUT;
1972				(void) setsockopt(s, IPPROTO_IP, IP_TOS,
1973				    (char *)&tos, sizeof(int));
1974			}
1975			break;
1976#endif
1977		}
1978		/* Set keepalives on the socket to detect dropped conns. */
1979#ifdef SO_KEEPALIVE
1980		keepalive = 1;
1981		(void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1982		    (char *)&keepalive, sizeof(int));
1983#endif
1984		reply(150, "Opening %s mode data connection for '%s'%s.",
1985		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1986		return (fdopen(pdata, fmode));
1987	}
1988	if (data >= 0) {
1989		reply(125, "Using existing data connection for '%s'%s.",
1990		    name, sizebuf);
1991		usedefault = 1;
1992		return (fdopen(data, fmode));
1993	}
1994	if (usedefault)
1995		data_dest = his_addr;
1996	usedefault = 1;
1997	retry = conerrno = 0;
1998	do {
1999		file = getdatasock(fmode);
2000		if (file == NULL) {
2001			char hbuf[NI_MAXHOST];
2002			char pbuf[NI_MAXSERV];
2003
2004			if (getnameinfo((struct sockaddr *)&data_source.si_su,
2005			    data_source.su_len, hbuf, sizeof(hbuf), pbuf,
2006			    sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV))
2007				strlcpy(hbuf, "?", sizeof(hbuf));
2008			reply(425, "Can't create data socket (%s,%s): %s.",
2009			      hbuf, pbuf, strerror(errno));
2010			return (NULL);
2011		}
2012		data = fileno(file);
2013		conerrno = 0;
2014		if (connect(data, (struct sockaddr *)&data_dest.si_su,
2015		    data_dest.su_len) == 0)
2016			break;
2017		conerrno = errno;
2018		(void) fclose(file);
2019		file = NULL;
2020		data = -1;
2021		if (conerrno == EADDRINUSE) {
2022			sleep((unsigned) swaitint);
2023			retry += swaitint;
2024		} else {
2025			break;
2026		}
2027	} while (retry <= swaitmax);
2028	if (conerrno != 0) {
2029		perror_reply(425, "Can't build data connection");
2030		return (NULL);
2031	}
2032	reply(150, "Opening %s mode data connection for '%s'%s.",
2033	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
2034	return (file);
2035}
2036
2037void
2038closedataconn(FILE *fd)
2039{
2040
2041	if (fd == NULL)
2042		return;
2043	(void)fclose(fd);
2044	data = -1;
2045	if (pdata >= 0)
2046		(void)close(pdata);
2047	pdata = -1;
2048}
2049
2050int
2051write_data(int fd, char *buf, size_t size, off_t *bufrem,
2052    struct timeval *then, int isdata)
2053{
2054	struct timeval now, td;
2055	ssize_t c;
2056
2057	while (size > 0) {
2058		c = size;
2059		if (curclass.writesize) {
2060			if (curclass.writesize < c)
2061				c = curclass.writesize;
2062		}
2063		if (curclass.rateget) {
2064			if (*bufrem < c)
2065				c = *bufrem;
2066		}
2067		(void) alarm(curclass.timeout);
2068		c = write(fd, buf, c);
2069		if (c <= 0)
2070			return (1);
2071		buf += c;
2072		size -= c;
2073		byte_count += c;
2074		if (isdata) {
2075			total_data_out += c;
2076			total_data += c;
2077		}
2078		total_bytes_out += c;
2079		total_bytes += c;
2080		if (curclass.rateget) {
2081			*bufrem -= c;
2082			if (*bufrem == 0) {
2083				(void)gettimeofday(&now, NULL);
2084				timersub(&now, then, &td);
2085				if (td.tv_sec == 0) {
2086					usleep(1000000 - td.tv_usec);
2087					(void)gettimeofday(then, NULL);
2088				} else
2089					*then = now;
2090				*bufrem = curclass.rateget;
2091			}
2092		}
2093	}
2094	return (0);
2095}
2096
2097static enum send_status
2098send_data_with_read(int filefd, int netfd, const struct stat *st, int isdata)
2099{
2100	struct timeval then;
2101	off_t bufrem;
2102	size_t readsize;
2103	char *buf;
2104	int c, error;
2105
2106	if (curclass.readsize)
2107		readsize = curclass.readsize;
2108	else
2109		readsize = (size_t)st->st_blksize;
2110	if ((buf = malloc(readsize)) == NULL) {
2111		perror_reply(451, "Local resource failure: malloc");
2112		return (SS_NO_TRANSFER);
2113	}
2114
2115	if (curclass.rateget) {
2116		bufrem = curclass.rateget;
2117		(void)gettimeofday(&then, NULL);
2118	}
2119	while (1) {
2120		(void) alarm(curclass.timeout);
2121		c = read(filefd, buf, readsize);
2122		if (c == 0)
2123			error = SS_SUCCESS;
2124		else if (c < 0)
2125			error = SS_FILE_ERROR;
2126		else if (write_data(netfd, buf, c, &bufrem, &then, isdata))
2127			error = SS_DATA_ERROR;
2128		else if (urgflag && handleoobcmd())
2129			error = SS_ABORTED;
2130		else
2131			continue;
2132
2133		free(buf);
2134		return (error);
2135	}
2136}
2137
2138static enum send_status
2139send_data_with_mmap(int filefd, int netfd, const struct stat *st, int isdata)
2140{
2141	struct timeval then;
2142	off_t bufrem, filesize, off, origoff;
2143	size_t mapsize, winsize;
2144	int error, sendbufsize, sendlowat;
2145	void *win;
2146
2147	if (curclass.sendbufsize) {
2148		sendbufsize = curclass.sendbufsize;
2149		if (setsockopt(netfd, SOL_SOCKET, SO_SNDBUF,
2150		    &sendbufsize, sizeof(int)) == -1)
2151			syslog(LOG_WARNING, "setsockopt(SO_SNDBUF, %d): %m",
2152			    sendbufsize);
2153	}
2154
2155	if (curclass.sendlowat) {
2156		sendlowat = curclass.sendlowat;
2157		if (setsockopt(netfd, SOL_SOCKET, SO_SNDLOWAT,
2158		    &sendlowat, sizeof(int)) == -1)
2159			syslog(LOG_WARNING, "setsockopt(SO_SNDLOWAT, %d): %m",
2160			    sendlowat);
2161	}
2162
2163	winsize = curclass.mmapsize;
2164	filesize = st->st_size;
2165	if (ftpd_debug)
2166		syslog(LOG_INFO, "mmapsize = %ld, writesize = %ld",
2167		    (long)winsize, (long)curclass.writesize);
2168	if (winsize == 0)
2169		goto try_read;
2170
2171	off = lseek(filefd, (off_t)0, SEEK_CUR);
2172	if (off == -1)
2173		goto try_read;
2174
2175	origoff = off;
2176	if (curclass.rateget) {
2177		bufrem = curclass.rateget;
2178		(void)gettimeofday(&then, NULL);
2179	}
2180	while (1) {
2181		mapsize = MIN(filesize - off, winsize);
2182		if (mapsize == 0)
2183			break;
2184		win = mmap(NULL, mapsize, PROT_READ,
2185		    MAP_FILE|MAP_SHARED, filefd, off);
2186		if (win == MAP_FAILED) {
2187			if (off == origoff)
2188				goto try_read;
2189			return (SS_FILE_ERROR);
2190		}
2191		(void) madvise(win, mapsize, MADV_SEQUENTIAL);
2192		error = write_data(netfd, win, mapsize, &bufrem, &then,
2193		    isdata);
2194		(void) madvise(win, mapsize, MADV_DONTNEED);
2195		munmap(win, mapsize);
2196		if (urgflag && handleoobcmd())
2197			return (SS_ABORTED);
2198		if (error)
2199			return (SS_DATA_ERROR);
2200		off += mapsize;
2201	}
2202	return (SS_SUCCESS);
2203
2204 try_read:
2205	return (send_data_with_read(filefd, netfd, st, isdata));
2206}
2207
2208/*
2209 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
2210 * encapsulation of the data subject to Mode, Structure, and Type.
2211 *
2212 * NB: Form isn't handled.
2213 */
2214static int
2215send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
2216{
2217	int	 c, filefd, netfd, rval;
2218
2219	urgflag = 0;
2220	transflag = 1;
2221	rval = -1;
2222
2223	switch (type) {
2224
2225	case TYPE_A:
2226 /* XXXLUKEM: rate limit ascii send (get) */
2227		(void) alarm(curclass.timeout);
2228		while ((c = getc(instr)) != EOF) {
2229			if (urgflag && handleoobcmd())
2230				goto cleanup_send_data;
2231			byte_count++;
2232			if (c == '\n') {
2233				if (ferror(outstr))
2234					goto data_err;
2235				(void) putc('\r', outstr);
2236				if (isdata) {
2237					total_data_out++;
2238					total_data++;
2239				}
2240				total_bytes_out++;
2241				total_bytes++;
2242			}
2243			(void) putc(c, outstr);
2244			if (isdata) {
2245				total_data_out++;
2246				total_data++;
2247			}
2248			total_bytes_out++;
2249			total_bytes++;
2250			if ((byte_count % 4096) == 0)
2251				(void) alarm(curclass.timeout);
2252		}
2253		(void) alarm(0);
2254		fflush(outstr);
2255		if (ferror(instr))
2256			goto file_err;
2257		if (ferror(outstr))
2258			goto data_err;
2259		rval = 0;
2260		goto cleanup_send_data;
2261
2262	case TYPE_I:
2263	case TYPE_L:
2264		filefd = fileno(instr);
2265		netfd = fileno(outstr);
2266		switch (send_data_with_mmap(filefd, netfd, st, isdata)) {
2267
2268		case SS_SUCCESS:
2269			break;
2270
2271		case SS_ABORTED:
2272		case SS_NO_TRANSFER:
2273			goto cleanup_send_data;
2274
2275		case SS_FILE_ERROR:
2276			goto file_err;
2277
2278		case SS_DATA_ERROR:
2279			goto data_err;
2280		}
2281		rval = 0;
2282		goto cleanup_send_data;
2283
2284	default:
2285		reply(550, "Unimplemented TYPE %d in send_data", type);
2286		goto cleanup_send_data;
2287	}
2288
2289 data_err:
2290	(void) alarm(0);
2291	perror_reply(426, "Data connection");
2292	goto cleanup_send_data;
2293
2294 file_err:
2295	(void) alarm(0);
2296	perror_reply(551, "Error on input file");
2297	goto cleanup_send_data;
2298
2299 cleanup_send_data:
2300	(void) alarm(0);
2301	transflag = 0;
2302	urgflag = 0;
2303	if (isdata) {
2304		total_files_out++;
2305		total_files++;
2306	}
2307	total_xfers_out++;
2308	total_xfers++;
2309	return (rval);
2310}
2311
2312/*
2313 * Transfer data from peer to "outstr" using the appropriate encapulation of
2314 * the data subject to Mode, Structure, and Type.
2315 *
2316 * N.B.: Form isn't handled.
2317 */
2318static int
2319receive_data(FILE *instr, FILE *outstr)
2320{
2321	int	c, bare_lfs, netfd, filefd, rval;
2322	off_t	byteswritten;
2323	char	*buf;
2324	size_t	readsize;
2325	struct sigaction sa, sa_saved;
2326	struct stat st;
2327#ifdef __GNUC__
2328	(void) &bare_lfs;
2329#endif
2330
2331	memset(&sa, 0, sizeof(sa));
2332	sigfillset(&sa.sa_mask);
2333	sa.sa_flags = SA_RESTART;
2334	sa.sa_handler = lostconn;
2335	(void) sigaction(SIGALRM, &sa, &sa_saved);
2336
2337	bare_lfs = 0;
2338	urgflag = 0;
2339	transflag = 1;
2340	rval = -1;
2341	byteswritten = 0;
2342	buf = NULL;
2343
2344#define FILESIZECHECK(x) \
2345			do { \
2346				if (curclass.maxfilesize != -1 && \
2347				    (x) > curclass.maxfilesize) { \
2348					errno = EFBIG; \
2349					goto file_err; \
2350				} \
2351			} while (0)
2352
2353	switch (type) {
2354
2355	case TYPE_I:
2356	case TYPE_L:
2357		netfd = fileno(instr);
2358		filefd = fileno(outstr);
2359		(void) alarm(curclass.timeout);
2360		if (curclass.readsize)
2361			readsize = curclass.readsize;
2362		else if (fstat(filefd, &st))
2363			readsize = (size_t)st.st_blksize;
2364		else
2365			readsize = BUFSIZ;
2366		if ((buf = malloc(readsize)) == NULL) {
2367			perror_reply(451, "Local resource failure: malloc");
2368			goto cleanup_recv_data;
2369		}
2370		if (curclass.rateput) {
2371			while (1) {
2372				int d;
2373				struct timeval then, now, td;
2374				off_t bufrem;
2375
2376				(void)gettimeofday(&then, NULL);
2377				errno = c = d = 0;
2378				for (bufrem = curclass.rateput; bufrem > 0; ) {
2379					if ((c = read(netfd, buf,
2380					    MIN(readsize, bufrem))) <= 0)
2381						goto recvdone;
2382					if (urgflag && handleoobcmd())
2383						goto cleanup_recv_data;
2384					FILESIZECHECK(byte_count + c);
2385					if ((d = write(filefd, buf, c)) != c)
2386						goto file_err;
2387					(void) alarm(curclass.timeout);
2388					bufrem -= c;
2389					byte_count += c;
2390					total_data_in += c;
2391					total_data += c;
2392					total_bytes_in += c;
2393					total_bytes += c;
2394				}
2395				(void)gettimeofday(&now, NULL);
2396				timersub(&now, &then, &td);
2397				if (td.tv_sec == 0)
2398					usleep(1000000 - td.tv_usec);
2399			}
2400		} else {
2401			while ((c = read(netfd, buf, readsize)) > 0) {
2402				if (urgflag && handleoobcmd())
2403					goto cleanup_recv_data;
2404				FILESIZECHECK(byte_count + c);
2405				if (write(filefd, buf, c) != c)
2406					goto file_err;
2407				(void) alarm(curclass.timeout);
2408				byte_count += c;
2409				total_data_in += c;
2410				total_data += c;
2411				total_bytes_in += c;
2412				total_bytes += c;
2413			}
2414		}
2415 recvdone:
2416		if (c < 0)
2417			goto data_err;
2418		rval = 0;
2419		goto cleanup_recv_data;
2420
2421	case TYPE_E:
2422		reply(553, "TYPE E not implemented.");
2423		goto cleanup_recv_data;
2424
2425	case TYPE_A:
2426		(void) alarm(curclass.timeout);
2427 /* XXXLUKEM: rate limit ascii receive (put) */
2428		while ((c = getc(instr)) != EOF) {
2429			if (urgflag && handleoobcmd())
2430				goto cleanup_recv_data;
2431			byte_count++;
2432			total_data_in++;
2433			total_data++;
2434			total_bytes_in++;
2435			total_bytes++;
2436			if ((byte_count % 4096) == 0)
2437				(void) alarm(curclass.timeout);
2438			if (c == '\n')
2439				bare_lfs++;
2440			while (c == '\r') {
2441				if (ferror(outstr))
2442					goto data_err;
2443				if ((c = getc(instr)) != '\n') {
2444					byte_count++;
2445					total_data_in++;
2446					total_data++;
2447					total_bytes_in++;
2448					total_bytes++;
2449					if ((byte_count % 4096) == 0)
2450						(void) alarm(curclass.timeout);
2451					byteswritten++;
2452					FILESIZECHECK(byteswritten);
2453					(void) putc ('\r', outstr);
2454					if (c == '\0' || c == EOF)
2455						goto contin2;
2456				}
2457			}
2458			byteswritten++;
2459			FILESIZECHECK(byteswritten);
2460			(void) putc(c, outstr);
2461 contin2:	;
2462		}
2463		(void) alarm(0);
2464		fflush(outstr);
2465		if (ferror(instr))
2466			goto data_err;
2467		if (ferror(outstr))
2468			goto file_err;
2469		if (bare_lfs) {
2470			reply(-226,
2471			    "WARNING! %d bare linefeeds received in ASCII mode",
2472			    bare_lfs);
2473			reply(0, "File may not have transferred correctly.");
2474		}
2475		rval = 0;
2476		goto cleanup_recv_data;
2477
2478	default:
2479		reply(550, "Unimplemented TYPE %d in receive_data", type);
2480		goto cleanup_recv_data;
2481	}
2482#undef FILESIZECHECK
2483
2484 data_err:
2485	(void) alarm(0);
2486	perror_reply(426, "Data Connection");
2487	goto cleanup_recv_data;
2488
2489 file_err:
2490	(void) alarm(0);
2491	perror_reply(452, "Error writing file");
2492	goto cleanup_recv_data;
2493
2494 cleanup_recv_data:
2495	(void) alarm(0);
2496	(void) sigaction(SIGALRM, &sa_saved, NULL);
2497	if (buf)
2498		free(buf);
2499	transflag = 0;
2500	urgflag = 0;
2501	total_files_in++;
2502	total_files++;
2503	total_xfers_in++;
2504	total_xfers++;
2505	return (rval);
2506}
2507
2508void
2509statcmd(void)
2510{
2511	struct sockinet *su = NULL;
2512	static char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
2513	u_char *a, *p;
2514	int ispassive, af;
2515	off_t otbi, otbo, otb;
2516
2517	a = p = (u_char *)NULL;
2518
2519	reply(-211, "%s FTP server status:", hostname);
2520	reply(0, "Version: %s", EMPTYSTR(version) ? "<suppressed>" : version);
2521	hbuf[0] = '\0';
2522	if (!getnameinfo((struct sockaddr *)&his_addr.si_su, his_addr.su_len,
2523			hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)
2524	    && strcmp(remotehost, hbuf) != 0)
2525		reply(0, "Connected to %s (%s)", remotehost, hbuf);
2526	else
2527		reply(0, "Connected to %s", remotehost);
2528
2529	if (logged_in) {
2530		if (curclass.type == CLASS_GUEST)
2531			reply(0, "Logged in anonymously");
2532		else
2533			reply(0, "Logged in as %s%s", pw->pw_name,
2534			    curclass.type == CLASS_CHROOT ? " (chroot)" : "");
2535	} else if (askpasswd)
2536		reply(0, "Waiting for password");
2537	else
2538		reply(0, "Waiting for user name");
2539	cprintf(stdout, "    TYPE: %s", typenames[type]);
2540	if (type == TYPE_A || type == TYPE_E)
2541		cprintf(stdout, ", FORM: %s", formnames[form]);
2542	if (type == TYPE_L) {
2543#if NBBY == 8
2544		cprintf(stdout, " %d", NBBY);
2545#else
2546			/* XXX: `bytesize' needs to be defined in this case */
2547		cprintf(stdout, " %d", bytesize);
2548#endif
2549	}
2550	cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n",
2551	    strunames[stru], modenames[mode]);
2552	ispassive = 0;
2553	if (data != -1) {
2554		reply(0, "Data connection open");
2555		su = NULL;
2556	} else if (pdata != -1) {
2557		reply(0, "in Passive mode");
2558		if (curclass.advertise.su_len != 0)
2559			su = &curclass.advertise;
2560		else
2561			su = &pasv_addr;
2562		ispassive = 1;
2563		goto printaddr;
2564	} else if (usedefault == 0) {
2565		su = (struct sockinet *)&data_dest;
2566
2567		if (epsvall) {
2568			reply(0, "EPSV only mode (EPSV ALL)");
2569			goto epsvonly;
2570		}
2571 printaddr:
2572							/* PASV/PORT */
2573		if (su->su_family == AF_INET) {
2574			a = (u_char *) &su->su_addr;
2575			p = (u_char *) &su->su_port;
2576#define UC(b) (((int) b) & 0xff)
2577			reply(0, "%s (%d,%d,%d,%d,%d,%d)",
2578				ispassive ? "PASV" : "PORT" ,
2579				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2580				UC(p[0]), UC(p[1]));
2581		}
2582
2583							/* LPSV/LPRT */
2584	    {
2585		int alen, i;
2586
2587		alen = 0;
2588		switch (su->su_family) {
2589		case AF_INET:
2590			a = (u_char *) &su->su_addr;
2591			p = (u_char *) &su->su_port;
2592			alen = sizeof(su->su_addr);
2593			af = 4;
2594			break;
2595#ifdef INET6
2596		case AF_INET6:
2597			a = (u_char *) &su->su_6addr;
2598			p = (u_char *) &su->su_port;
2599			alen = sizeof(su->su_6addr);
2600			af = 6;
2601			break;
2602#endif
2603		default:
2604			af = 0;
2605			break;
2606		}
2607		if (af) {
2608			cprintf(stdout, "    %s (%d,%d",
2609			    ispassive ? "LPSV" : "LPRT", af, alen);
2610			for (i = 0; i < alen; i++)
2611				cprintf(stdout, ",%d", UC(a[i]));
2612			cprintf(stdout, ",%d,%d,%d)\r\n",
2613			    2, UC(p[0]), UC(p[1]));
2614#undef UC
2615		}
2616	    }
2617
2618		/* EPRT/EPSV */
2619 epsvonly:
2620		af = af2epsvproto(su->su_family);
2621		hbuf[0] = '\0';
2622		if (af > 0) {
2623			struct sockinet tmp;
2624
2625			tmp = *su;
2626#ifdef INET6
2627			if (tmp.su_family == AF_INET6)
2628				tmp.su_scope_id = 0;
2629#endif
2630			if (getnameinfo((struct sockaddr *)&tmp.si_su,
2631			    tmp.su_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
2632			    NI_NUMERICHOST | NI_NUMERICSERV) == 0)
2633				reply(0, "%s (|%d|%s|%s|)",
2634				    ispassive ? "EPSV" : "EPRT",
2635				    af, hbuf, sbuf);
2636		}
2637	} else
2638		reply(0, "No data connection");
2639
2640	if (logged_in) {
2641		reply(0,
2642		    "Data sent:        " LLF " byte%s in " LLF " file%s",
2643		    (LLT)total_data_out, PLURAL(total_data_out),
2644		    (LLT)total_files_out, PLURAL(total_files_out));
2645		reply(0,
2646		    "Data received:    " LLF " byte%s in " LLF " file%s",
2647		    (LLT)total_data_in, PLURAL(total_data_in),
2648		    (LLT)total_files_in, PLURAL(total_files_in));
2649		reply(0,
2650		    "Total data:       " LLF " byte%s in " LLF " file%s",
2651		    (LLT)total_data, PLURAL(total_data),
2652		    (LLT)total_files, PLURAL(total_files));
2653	}
2654	otbi = total_bytes_in;
2655	otbo = total_bytes_out;
2656	otb = total_bytes;
2657	reply(0, "Traffic sent:     " LLF " byte%s in " LLF " transfer%s",
2658	    (LLT)otbo, PLURAL(otbo),
2659	    (LLT)total_xfers_out, PLURAL(total_xfers_out));
2660	reply(0, "Traffic received: " LLF " byte%s in " LLF " transfer%s",
2661	    (LLT)otbi, PLURAL(otbi),
2662	    (LLT)total_xfers_in, PLURAL(total_xfers_in));
2663	reply(0, "Total traffic:    " LLF " byte%s in " LLF " transfer%s",
2664	    (LLT)otb, PLURAL(otb),
2665	    (LLT)total_xfers, PLURAL(total_xfers));
2666
2667	if (logged_in && !CURCLASS_FLAGS_ISSET(private)) {
2668		struct ftpconv *cp;
2669
2670		reply(0, "%s", "");
2671		reply(0, "Class: %s, type: %s",
2672		    curclass.classname, CURCLASSTYPE);
2673		reply(0, "Check PORT/LPRT commands: %sabled",
2674		    CURCLASS_FLAGS_ISSET(checkportcmd) ? "en" : "dis");
2675		if (! EMPTYSTR(curclass.display))
2676			reply(0, "Display file: %s", curclass.display);
2677		if (! EMPTYSTR(curclass.notify))
2678			reply(0, "Notify fileglob: %s", curclass.notify);
2679		reply(0, "Idle timeout: " LLF ", maximum timeout: " LLF,
2680		    (LLT)curclass.timeout, (LLT)curclass.maxtimeout);
2681		reply(0, "Current connections: %d", connections);
2682		if (curclass.limit == -1)
2683			reply(0, "Maximum connections: unlimited");
2684		else
2685			reply(0, "Maximum connections: " LLF,
2686			    (LLT)curclass.limit);
2687		if (curclass.limitfile)
2688			reply(0, "Connection limit exceeded message file: %s",
2689			    conffilename(curclass.limitfile));
2690		if (! EMPTYSTR(curclass.chroot))
2691			reply(0, "Chroot format: %s", curclass.chroot);
2692		reply(0, "Deny bad ftpusers(5) quickly: %sabled",
2693		    CURCLASS_FLAGS_ISSET(denyquick) ? "en" : "dis");
2694		if (! EMPTYSTR(curclass.homedir))
2695			reply(0, "Homedir format: %s", curclass.homedir);
2696		if (curclass.maxfilesize == -1)
2697			reply(0, "Maximum file size: unlimited");
2698		else
2699			reply(0, "Maximum file size: " LLF,
2700			    (LLT)curclass.maxfilesize);
2701		if (! EMPTYSTR(curclass.motd))
2702			reply(0, "MotD file: %s", conffilename(curclass.motd));
2703		reply(0,
2704	    "Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
2705		    CURCLASS_FLAGS_ISSET(modify) ? "en" : "dis");
2706		reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
2707		    CURCLASS_FLAGS_ISSET(upload) ? "en" : "dis");
2708		reply(0, "Sanitize file names: %sabled",
2709		    CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis");
2710		reply(0, "PASV/LPSV/EPSV connections: %sabled",
2711		    CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis");
2712		if (curclass.advertise.su_len != 0) {
2713			char buf[50];	/* big enough for IPv6 address */
2714			const char *bp;
2715
2716			bp = inet_ntop(curclass.advertise.su_family,
2717			    (void *)&curclass.advertise.su_addr,
2718			    buf, sizeof(buf));
2719			if (bp != NULL)
2720				reply(0, "PASV advertise address: %s", bp);
2721		}
2722		if (curclass.portmin && curclass.portmax)
2723			reply(0, "PASV port range: " LLF " - " LLF,
2724			    (LLT)curclass.portmin, (LLT)curclass.portmax);
2725		if (curclass.rateget)
2726			reply(0, "Rate get limit: " LLF " bytes/sec",
2727			    (LLT)curclass.rateget);
2728		else
2729			reply(0, "Rate get limit: disabled");
2730		if (curclass.rateput)
2731			reply(0, "Rate put limit: " LLF " bytes/sec",
2732			    (LLT)curclass.rateput);
2733		else
2734			reply(0, "Rate put limit: disabled");
2735		if (curclass.mmapsize)
2736			reply(0, "Mmap size: " LLF, (LLT)curclass.mmapsize);
2737		else
2738			reply(0, "Mmap size: disabled");
2739		if (curclass.readsize)
2740			reply(0, "Read size: " LLF, (LLT)curclass.readsize);
2741		else
2742			reply(0, "Read size: default");
2743		if (curclass.writesize)
2744			reply(0, "Write size: " LLF, (LLT)curclass.writesize);
2745		else
2746			reply(0, "Write size: default");
2747		if (curclass.recvbufsize)
2748			reply(0, "Receive buffer size: " LLF,
2749			    (LLT)curclass.recvbufsize);
2750		else
2751			reply(0, "Receive buffer size: default");
2752		if (curclass.sendbufsize)
2753			reply(0, "Send buffer size: " LLF,
2754			    (LLT)curclass.sendbufsize);
2755		else
2756			reply(0, "Send buffer size: default");
2757		if (curclass.sendlowat)
2758			reply(0, "Send low water mark: " LLF,
2759			    (LLT)curclass.sendlowat);
2760		else
2761			reply(0, "Send low water mark: default");
2762		reply(0, "Umask: %.04o", curclass.umask);
2763		for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
2764			if (cp->suffix == NULL || cp->types == NULL ||
2765			    cp->command == NULL)
2766				continue;
2767			reply(0, "Conversion: %s [%s] disable: %s, command: %s",
2768			    cp->suffix, cp->types, cp->disable, cp->command);
2769		}
2770	}
2771
2772	reply(211, "End of status");
2773}
2774
2775void
2776fatal(const char *s)
2777{
2778
2779	reply(451, "Error in server: %s\n", s);
2780	reply(221, "Closing connection due to server error.");
2781	dologout(0);
2782	/* NOTREACHED */
2783}
2784
2785/*
2786 * reply() --
2787 *	depending on the value of n, display fmt with a trailing CRLF and
2788 *	prefix of:
2789 *	n < -1		prefix the message with abs(n) + "-"	(initial line)
2790 *	n == 0		prefix the message with 4 spaces	(middle lines)
2791 *	n >  0		prefix the message with n + " "		(final line)
2792 */
2793void
2794reply(int n, const char *fmt, ...)
2795{
2796	char	msg[MAXPATHLEN * 2 + 100];
2797	size_t	b;
2798	va_list	ap;
2799
2800	b = 0;
2801	if (n == 0)
2802		b = snprintf(msg, sizeof(msg), "    ");
2803	else if (n < 0)
2804		b = snprintf(msg, sizeof(msg), "%d-", -n);
2805	else
2806		b = snprintf(msg, sizeof(msg), "%d ", n);
2807	va_start(ap, fmt);
2808	vsnprintf(msg + b, sizeof(msg) - b, fmt, ap);
2809	va_end(ap);
2810	cprintf(stdout, "%s\r\n", msg);
2811	(void)fflush(stdout);
2812	if (ftpd_debug)
2813		syslog(LOG_DEBUG, "<--- %s", msg);
2814}
2815
2816static void
2817logremotehost(struct sockinet *who)
2818{
2819
2820	if (getnameinfo((struct sockaddr *)&who->si_su,
2821	    who->su_len, remotehost, sizeof(remotehost), NULL, 0, 0))
2822		strlcpy(remotehost, "?", sizeof(remotehost));
2823
2824#if HAVE_SETPROCTITLE
2825	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
2826	setproctitle("%s", proctitle);
2827#endif /* HAVE_SETPROCTITLE */
2828	if (logging)
2829		syslog(LOG_INFO, "connection from %s to %s",
2830		    remotehost, hostname);
2831}
2832
2833/*
2834 * Record logout in wtmp file and exit with supplied status.
2835 * NOTE: because this is called from signal handlers it cannot
2836 *       use stdio (or call other functions that use stdio).
2837 */
2838void
2839dologout(int status)
2840{
2841	/*
2842	* Prevent reception of SIGURG from resulting in a resumption
2843	* back to the main program loop.
2844	*/
2845	transflag = 0;
2846	logout_utmp();
2847	if (logged_in) {
2848#ifdef KERBEROS
2849		if (!notickets && krbtkfile_env)
2850			unlink(krbtkfile_env);
2851#endif
2852	}
2853	/* beware of flushing buffers after a SIGPIPE */
2854	if (xferlogfd != -1)
2855		close(xferlogfd);
2856	_exit(status);
2857}
2858
2859void
2860abor(void)
2861{
2862
2863	if (!transflag)
2864		return;
2865	tmpline[0] = '\0';
2866	is_oob = 0;
2867	reply(426, "Transfer aborted. Data connection closed.");
2868	reply(226, "Abort successful");
2869	transflag = 0;		/* flag that the transfer has aborted */
2870}
2871
2872void
2873statxfer(void)
2874{
2875
2876	if (!transflag)
2877		return;
2878	tmpline[0] = '\0';
2879	is_oob = 0;
2880	if (file_size != (off_t) -1)
2881		reply(213,
2882		    "Status: " LLF " of " LLF " byte%s transferred",
2883		    (LLT)byte_count, (LLT)file_size,
2884		    PLURAL(byte_count));
2885	else
2886		reply(213, "Status: " LLF " byte%s transferred",
2887		    (LLT)byte_count, PLURAL(byte_count));
2888}
2889
2890/*
2891 * Call when urgflag != 0 to handle Out Of Band commands.
2892 * Returns non zero if the OOB command aborted the transfer
2893 * by setting transflag to 0. (c.f., "ABOR").
2894 */
2895static int
2896handleoobcmd()
2897{
2898	char *cp;
2899	int ret;
2900
2901	if (!urgflag)
2902		return (0);
2903	urgflag = 0;
2904	/* only process if transfer occurring */
2905	if (!transflag)
2906		return (0);
2907	cp = tmpline;
2908	ret = getline(cp, sizeof(tmpline)-1, stdin);
2909	if (ret == -1) {
2910		reply(221, "You could at least say goodbye.");
2911		dologout(0);
2912	} else if (ret == -2) {
2913		/* Ignore truncated command */
2914		/* XXX: abort xfer with "500 command too long", & return 1 ? */
2915		return 0;
2916	}
2917		/*
2918		 * Manually parse OOB commands, because we can't
2919		 * recursively call the yacc parser...
2920		 */
2921	if (strcasecmp(cp, "ABOR\r\n") == 0) {
2922		abor();
2923	} else if (strcasecmp(cp, "STAT\r\n") == 0) {
2924		statxfer();
2925	} else {
2926		/* XXX: error with "500 unknown command" ? */
2927	}
2928	return (transflag == 0);
2929}
2930
2931static int
2932bind_pasv_addr(void)
2933{
2934	static int passiveport;
2935	int port, len;
2936
2937	len = pasv_addr.su_len;
2938	if (curclass.portmin == 0 && curclass.portmax == 0) {
2939		pasv_addr.su_port = 0;
2940		return (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len));
2941	}
2942
2943	if (passiveport == 0) {
2944		srand(getpid());
2945		passiveport = rand() % (curclass.portmax - curclass.portmin)
2946		    + curclass.portmin;
2947	}
2948
2949	port = passiveport;
2950	while (1) {
2951		port++;
2952		if (port > curclass.portmax)
2953			port = curclass.portmin;
2954		else if (port == passiveport) {
2955			errno = EAGAIN;
2956			return (-1);
2957		}
2958		pasv_addr.su_port = htons(port);
2959		if (bind(pdata, (struct sockaddr *)&pasv_addr.si_su, len) == 0)
2960			break;
2961		if (errno != EADDRINUSE)
2962			return (-1);
2963	}
2964	passiveport = port;
2965	return (0);
2966}
2967
2968/*
2969 * Note: a response of 425 is not mentioned as a possible response to
2970 *	the PASV command in RFC959. However, it has been blessed as
2971 *	a legitimate response by Jon Postel in a telephone conversation
2972 *	with Rick Adams on 25 Jan 89.
2973 */
2974void
2975passive(void)
2976{
2977	socklen_t len, recvbufsize;
2978	char *p, *a;
2979
2980	if (pdata >= 0)
2981		close(pdata);
2982	pdata = socket(AF_INET, SOCK_STREAM, 0);
2983	if (pdata < 0 || !logged_in) {
2984		perror_reply(425, "Can't open passive connection");
2985		return;
2986	}
2987	pasv_addr = ctrl_addr;
2988
2989	if (bind_pasv_addr() < 0)
2990		goto pasv_error;
2991	len = pasv_addr.su_len;
2992	if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
2993		goto pasv_error;
2994	pasv_addr.su_len = len;
2995	if (curclass.recvbufsize) {
2996		recvbufsize = curclass.recvbufsize;
2997		if (setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, &recvbufsize,
2998			       sizeof(int)) == -1)
2999			syslog(LOG_WARNING, "setsockopt(SO_RCVBUF, %d): %m",
3000			       recvbufsize);
3001	}
3002	if (listen(pdata, 1) < 0)
3003		goto pasv_error;
3004	if (curclass.advertise.su_len != 0)
3005		a = (char *) &curclass.advertise.su_addr;
3006	else
3007		a = (char *) &pasv_addr.su_addr;
3008	p = (char *) &pasv_addr.su_port;
3009
3010#define UC(b) (((int) b) & 0xff)
3011
3012	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
3013		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
3014	return;
3015
3016 pasv_error:
3017	(void) close(pdata);
3018	pdata = -1;
3019	perror_reply(425, "Can't open passive connection");
3020	return;
3021}
3022
3023/*
3024 * convert protocol identifier to/from AF
3025 */
3026int
3027lpsvproto2af(int proto)
3028{
3029
3030	switch (proto) {
3031	case 4:
3032		return AF_INET;
3033#ifdef INET6
3034	case 6:
3035		return AF_INET6;
3036#endif
3037	default:
3038		return -1;
3039	}
3040}
3041
3042int
3043af2lpsvproto(int af)
3044{
3045
3046	switch (af) {
3047	case AF_INET:
3048		return 4;
3049#ifdef INET6
3050	case AF_INET6:
3051		return 6;
3052#endif
3053	default:
3054		return -1;
3055	}
3056}
3057
3058int
3059epsvproto2af(int proto)
3060{
3061
3062	switch (proto) {
3063	case 1:
3064		return AF_INET;
3065#ifdef INET6
3066	case 2:
3067		return AF_INET6;
3068#endif
3069	default:
3070		return -1;
3071	}
3072}
3073
3074int
3075af2epsvproto(int af)
3076{
3077
3078	switch (af) {
3079	case AF_INET:
3080		return 1;
3081#ifdef INET6
3082	case AF_INET6:
3083		return 2;
3084#endif
3085	default:
3086		return -1;
3087	}
3088}
3089
3090/*
3091 * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
3092 * 229 Entering Extended Passive Mode (|||port|)
3093 */
3094void
3095long_passive(char *cmd, int pf)
3096{
3097	socklen_t len;
3098	char *p, *a;
3099
3100	if (!logged_in) {
3101		syslog(LOG_NOTICE, "long passive but not logged in");
3102		reply(503, "Login with USER first.");
3103		return;
3104	}
3105
3106	if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
3107		/*
3108		 * XXX: only EPRT/EPSV ready clients will understand this
3109		 */
3110		if (strcmp(cmd, "EPSV") != 0)
3111			reply(501, "Network protocol mismatch"); /*XXX*/
3112		else
3113			epsv_protounsupp("Network protocol mismatch");
3114
3115		return;
3116	}
3117
3118	if (pdata >= 0)
3119		close(pdata);
3120	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
3121	if (pdata < 0) {
3122		perror_reply(425, "Can't open passive connection");
3123		return;
3124	}
3125	pasv_addr = ctrl_addr;
3126	if (bind_pasv_addr() < 0)
3127		goto pasv_error;
3128	len = pasv_addr.su_len;
3129	if (getsockname(pdata, (struct sockaddr *) &pasv_addr.si_su, &len) < 0)
3130		goto pasv_error;
3131	pasv_addr.su_len = len;
3132	if (listen(pdata, 1) < 0)
3133		goto pasv_error;
3134	p = (char *) &pasv_addr.su_port;
3135
3136#define UC(b) (((int) b) & 0xff)
3137
3138	if (strcmp(cmd, "LPSV") == 0) {
3139		struct sockinet *advert;
3140
3141		if (curclass.advertise.su_len != 0)
3142			advert = &curclass.advertise;
3143		else
3144			advert = &pasv_addr;
3145		switch (advert->su_family) {
3146		case AF_INET:
3147			a = (char *) &advert->su_addr;
3148			reply(228,
3149    "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3150				4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
3151				2, UC(p[0]), UC(p[1]));
3152			return;
3153#ifdef INET6
3154		case AF_INET6:
3155			a = (char *) &advert->su_6addr;
3156			reply(228,
3157    "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
3158				6, 16,
3159				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
3160				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
3161				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
3162				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
3163				2, UC(p[0]), UC(p[1]));
3164			return;
3165#endif
3166		}
3167#undef UC
3168	} else if (strcmp(cmd, "EPSV") == 0) {
3169		switch (pasv_addr.su_family) {
3170		case AF_INET:
3171#ifdef INET6
3172		case AF_INET6:
3173#endif
3174			reply(229, "Entering Extended Passive Mode (|||%d|)",
3175			    ntohs(pasv_addr.su_port));
3176			return;
3177		}
3178	} else {
3179		/* more proper error code? */
3180	}
3181
3182 pasv_error:
3183	(void) close(pdata);
3184	pdata = -1;
3185	perror_reply(425, "Can't open passive connection");
3186	return;
3187}
3188
3189int
3190extended_port(const char *arg)
3191{
3192	char *tmp = NULL;
3193	char *result[3];
3194	char *p, *q;
3195	char delim;
3196	struct addrinfo hints;
3197	struct addrinfo *res = NULL;
3198	int i;
3199	unsigned long proto;
3200
3201	tmp = ftpd_strdup(arg);
3202	p = tmp;
3203	delim = p[0];
3204	p++;
3205	memset(result, 0, sizeof(result));
3206	for (i = 0; i < 3; i++) {
3207		q = strchr(p, delim);
3208		if (!q || *q != delim)
3209			goto parsefail;
3210		*q++ = '\0';
3211		result[i] = p;
3212		p = q;
3213	}
3214
3215			/* some more sanity checks */
3216	errno = 0;
3217	p = NULL;
3218	(void)strtoul(result[2], &p, 10);
3219	if (errno || !*result[2] || *p)
3220		goto parsefail;
3221	errno = 0;
3222	p = NULL;
3223	proto = strtoul(result[0], &p, 10);
3224	if (errno || !*result[0] || *p)
3225		goto protounsupp;
3226
3227	memset(&hints, 0, sizeof(hints));
3228	hints.ai_family = epsvproto2af((int)proto);
3229	if (hints.ai_family < 0)
3230		goto protounsupp;
3231	hints.ai_socktype = SOCK_STREAM;
3232	hints.ai_flags = AI_NUMERICHOST;
3233	if (getaddrinfo(result[1], result[2], &hints, &res))
3234		goto parsefail;
3235	if (res->ai_next)
3236		goto parsefail;
3237	if (sizeof(data_dest) < res->ai_addrlen)
3238		goto parsefail;
3239	memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen);
3240	data_dest.su_len = res->ai_addrlen;
3241#ifdef INET6
3242	if (his_addr.su_family == AF_INET6 &&
3243	    data_dest.su_family == AF_INET6) {
3244			/* XXX: more sanity checks! */
3245		data_dest.su_scope_id = his_addr.su_scope_id;
3246	}
3247#endif
3248
3249	if (tmp != NULL)
3250		free(tmp);
3251	if (res)
3252		freeaddrinfo(res);
3253	return 0;
3254
3255 parsefail:
3256	reply(500, "Invalid argument, rejected.");
3257	usedefault = 1;
3258	if (tmp != NULL)
3259		free(tmp);
3260	if (res)
3261		freeaddrinfo(res);
3262	return -1;
3263
3264 protounsupp:
3265	epsv_protounsupp("Protocol not supported");
3266	usedefault = 1;
3267	if (tmp != NULL)
3268		free(tmp);
3269	return -1;
3270}
3271
3272/*
3273 * 522 Protocol not supported (proto,...)
3274 * as we assume address family for control and data connections are the same,
3275 * we do not return the list of address families we support - instead, we
3276 * return the address family of the control connection.
3277 */
3278void
3279epsv_protounsupp(const char *message)
3280{
3281	int proto;
3282
3283	proto = af2epsvproto(ctrl_addr.su_family);
3284	if (proto < 0)
3285		reply(501, "%s", message);	/* XXX */
3286	else
3287		reply(522, "%s, use (%d)", message, proto);
3288}
3289
3290/*
3291 * Generate unique name for file with basename "local".
3292 * The file named "local" is already known to exist.
3293 * Generates failure reply on error.
3294 *
3295 * XXX:	this function should under go changes similar to
3296 *	the mktemp(3)/mkstemp(3) changes.
3297 */
3298static char *
3299gunique(const char *local)
3300{
3301	static char new[MAXPATHLEN];
3302	struct stat st;
3303	char *cp;
3304	int count;
3305
3306	cp = strrchr(local, '/');
3307	if (cp)
3308		*cp = '\0';
3309	if (stat(cp ? local : ".", &st) < 0) {
3310		perror_reply(553, cp ? local : ".");
3311		return (NULL);
3312	}
3313	if (cp)
3314		*cp = '/';
3315	for (count = 1; count < 100; count++) {
3316		(void)snprintf(new, sizeof(new) - 1, "%s.%d", local, count);
3317		if (stat(new, &st) < 0)
3318			return (new);
3319	}
3320	reply(452, "Unique file name cannot be created.");
3321	return (NULL);
3322}
3323
3324/*
3325 * Format and send reply containing system error number.
3326 */
3327void
3328perror_reply(int code, const char *string)
3329{
3330	int save_errno;
3331
3332	save_errno = errno;
3333	reply(code, "%s: %s.", string, strerror(errno));
3334	errno = save_errno;
3335}
3336
3337static char *onefile[] = {
3338	"",
3339	0
3340};
3341
3342void
3343send_file_list(const char *whichf)
3344{
3345	struct stat st;
3346	DIR *dirp = NULL;
3347	struct dirent *dir;
3348	FILE *dout = NULL;
3349	char **dirlist, *dirname, *p;
3350	char *notglob = NULL;
3351	int simple = 0;
3352	int freeglob = 0;
3353	glob_t gl;
3354
3355#ifdef __GNUC__
3356	(void) &dout;
3357	(void) &dirlist;
3358	(void) &simple;
3359	(void) &freeglob;
3360#endif
3361	urgflag = 0;
3362
3363	p = NULL;
3364	if (strpbrk(whichf, "~{[*?") != NULL) {
3365		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE|GLOB_LIMIT;
3366
3367		memset(&gl, 0, sizeof(gl));
3368		freeglob = 1;
3369		if (glob(whichf, flags, 0, &gl)) {
3370			reply(450, "Not found");
3371			goto cleanup_send_file_list;
3372		} else if (gl.gl_pathc == 0) {
3373			errno = ENOENT;
3374			perror_reply(450, whichf);
3375			goto cleanup_send_file_list;
3376		}
3377		dirlist = gl.gl_pathv;
3378	} else {
3379		notglob = ftpd_strdup(whichf);
3380		onefile[0] = notglob;
3381		dirlist = onefile;
3382		simple = 1;
3383	}
3384					/* XXX: } for vi sm */
3385
3386	while ((dirname = *dirlist++) != NULL) {
3387		int trailingslash = 0;
3388
3389		if (stat(dirname, &st) < 0) {
3390			/*
3391			 * If user typed "ls -l", etc, and the client
3392			 * used NLST, do what the user meant.
3393			 */
3394			/* XXX: nuke this support? */
3395			if (dirname[0] == '-' && *dirlist == NULL &&
3396			    transflag == 0) {
3397				char *argv[] = { INTERNAL_LS, "", NULL };
3398
3399				argv[1] = dirname;
3400				retrieve(argv, dirname);
3401				goto cleanup_send_file_list;
3402			}
3403			perror_reply(450, whichf);
3404			goto cleanup_send_file_list;
3405		}
3406
3407		if (S_ISREG(st.st_mode)) {
3408			/*
3409			 * XXXRFC:
3410			 *	should we follow RFC959 and not work
3411			 *	for non directories?
3412			 */
3413			if (dout == NULL) {
3414				dout = dataconn("file list", (off_t)-1, "w");
3415				if (dout == NULL)
3416					goto cleanup_send_file_list;
3417				transflag = 1;
3418			}
3419			cprintf(dout, "%s%s\n", dirname,
3420			    type == TYPE_A ? "\r" : "");
3421			continue;
3422		} else if (!S_ISDIR(st.st_mode))
3423			continue;
3424
3425		if (dirname[strlen(dirname) - 1] == '/')
3426			trailingslash++;
3427
3428		if ((dirp = opendir(dirname)) == NULL)
3429			continue;
3430
3431		while ((dir = readdir(dirp)) != NULL) {
3432			char nbuf[MAXPATHLEN];
3433
3434			if (urgflag && handleoobcmd())
3435				goto cleanup_send_file_list;
3436
3437			if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
3438				continue;
3439
3440			(void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
3441			    trailingslash ? "" : "/", dir->d_name);
3442
3443			/*
3444			 * We have to do a stat to ensure it's
3445			 * not a directory or special file.
3446			 */
3447			/*
3448			 * XXXRFC:
3449			 *	should we follow RFC959 and filter out
3450			 *	non files ?   lukem - NO!, or not until
3451			 *	our ftp client uses MLS{T,D} for completion.
3452			 */
3453			if (simple || (stat(nbuf, &st) == 0 &&
3454			    S_ISREG(st.st_mode))) {
3455				if (dout == NULL) {
3456					dout = dataconn("file list", (off_t)-1,
3457						"w");
3458					if (dout == NULL)
3459						goto cleanup_send_file_list;
3460					transflag = 1;
3461				}
3462				p = nbuf;
3463				if (nbuf[0] == '.' && nbuf[1] == '/')
3464					p = &nbuf[2];
3465				cprintf(dout, "%s%s\n", p,
3466				    type == TYPE_A ? "\r" : "");
3467			}
3468		}
3469		(void) closedir(dirp);
3470	}
3471
3472	if (dout == NULL)
3473		reply(450, "No files found.");
3474	else if (ferror(dout) != 0)
3475		perror_reply(451, "Data connection");
3476	else
3477		reply(226, "Transfer complete.");
3478
3479 cleanup_send_file_list:
3480	closedataconn(dout);
3481	transflag = 0;
3482	urgflag = 0;
3483	total_xfers++;
3484	total_xfers_out++;
3485	if (notglob)
3486		free(notglob);
3487	if (freeglob)
3488		globfree(&gl);
3489}
3490
3491char *
3492conffilename(const char *s)
3493{
3494	static char filename[MAXPATHLEN];
3495
3496	if (*s == '/')
3497		strlcpy(filename, s, sizeof(filename));
3498	else
3499		(void)snprintf(filename, sizeof(filename), "%s/%s", confdir ,s);
3500	return (filename);
3501}
3502
3503/*
3504 * logxfer --
3505 *	if logging > 1, then based on the arguments, syslog a message:
3506 *	 if bytes != -1		"<command> <file1> = <bytes> bytes"
3507 *	 else if file2 != NULL	"<command> <file1> <file2>"
3508 *	 else			"<command> <file1>"
3509 *	if elapsed != NULL, append "in xxx.yyy seconds"
3510 *	if error != NULL, append ": " + error
3511 *
3512 *	if doxferlog != 0, bytes != -1, and command is "get", "put",
3513 *	or "append", syslog and/or write a wu-ftpd style xferlog entry
3514 */
3515void
3516logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
3517    const struct timeval *elapsed, const char *error)
3518{
3519	char		 buf[MAXPATHLEN * 2 + 100];
3520	char		 realfile1[MAXPATHLEN], realfile2[MAXPATHLEN];
3521	const char	*r1, *r2;
3522	char		 direction;
3523	size_t		 len;
3524	time_t		 now;
3525
3526	if (logging <=1 && !doxferlog)
3527		return;
3528
3529	r1 = r2 = NULL;
3530	if ((r1 = realpath(file1, realfile1)) == NULL)
3531		r1 = file1;
3532	if (file2 != NULL)
3533		if ((r2 = realpath(file2, realfile2)) == NULL)
3534			r2 = file2;
3535
3536		/*
3537		 * syslog command
3538		 */
3539	if (logging > 1) {
3540		len = snprintf(buf, sizeof(buf), "%s %s", command, r1);
3541		if (bytes != (off_t)-1)
3542			len += snprintf(buf + len, sizeof(buf) - len,
3543			    " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes));
3544		else if (r2 != NULL)
3545			len += snprintf(buf + len, sizeof(buf) - len,
3546			    " %s", r2);
3547		if (elapsed != NULL)
3548			len += snprintf(buf + len, sizeof(buf) - len,
3549			    " in %ld.%.03d seconds", elapsed->tv_sec,
3550			    (int)(elapsed->tv_usec / 1000));
3551		if (error != NULL)
3552			len += snprintf(buf + len, sizeof(buf) - len,
3553			    ": %s", error);
3554		syslog(LOG_INFO, "%s", buf);
3555	}
3556
3557		/*
3558		 * syslog wu-ftpd style log entry, prefixed with "xferlog: "
3559		 */
3560	if (!doxferlog || bytes == -1)
3561		return;
3562
3563	if (strcmp(command, "get") == 0)
3564		direction = 'o';
3565	else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0)
3566		direction = 'i';
3567	else
3568		return;
3569
3570	time(&now);
3571	len = snprintf(buf, sizeof(buf),
3572	    "%.24s %ld %s " LLF " %s %c %s %c %c %s FTP 0 * %c\n",
3573
3574/*
3575 * XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
3576 *	the full date.  This may be problematic for accurate log parsing,
3577 *	given that syslog messages don't contain the full date.
3578 */
3579	    ctime(&now),
3580	    elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0),
3581	    remotehost,
3582	    (LLT) bytes,
3583	    r1,
3584	    type == TYPE_A ? 'a' : 'b',
3585	    "_",		/* XXX: take conversions into account? */
3586	    direction,
3587
3588	    curclass.type == CLASS_GUEST ?  'a' :
3589	    curclass.type == CLASS_CHROOT ? 'g' :
3590	    curclass.type == CLASS_REAL ?   'r' : '?',
3591
3592	    curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name,
3593	    error != NULL ? 'i' : 'c'
3594	    );
3595
3596	if ((doxferlog & 2) && xferlogfd != -1)
3597		write(xferlogfd, buf, len);
3598	if ((doxferlog & 1)) {
3599		buf[len-1] = '\n';	/* strip \n from syslog message */
3600		syslog(LOG_INFO, "xferlog: %s", buf);
3601	}
3602}
3603
3604/*
3605 * Log the resource usage.
3606 *
3607 * XXX: more resource usage to logging?
3608 */
3609void
3610logrusage(const struct rusage *rusage_before,
3611    const struct rusage *rusage_after)
3612{
3613	struct timeval usrtime, systime;
3614
3615	if (logging <= 1)
3616		return;
3617
3618	timersub(&rusage_after->ru_utime, &rusage_before->ru_utime, &usrtime);
3619	timersub(&rusage_after->ru_stime, &rusage_before->ru_stime, &systime);
3620	syslog(LOG_INFO, "%ld.%.03du %ld.%.03ds %ld+%ldio %ldpf+%ldw",
3621	    usrtime.tv_sec, (int)(usrtime.tv_usec / 1000),
3622	    systime.tv_sec, (int)(systime.tv_usec / 1000),
3623	    rusage_after->ru_inblock - rusage_before->ru_inblock,
3624	    rusage_after->ru_oublock - rusage_before->ru_oublock,
3625	    rusage_after->ru_majflt - rusage_before->ru_majflt,
3626	    rusage_after->ru_nswap - rusage_before->ru_nswap);
3627}
3628
3629/*
3630 * Determine if `password' is valid for user given in `pw'.
3631 * Returns 2 if password expired, 1 if otherwise failed, 0 if ok
3632 */
3633int
3634checkpassword(const struct passwd *pwent, const char *password)
3635{
3636	char	*orig, *new;
3637	time_t	 change, expire, now;
3638
3639	change = expire = 0;
3640	if (pwent == NULL)
3641		return 1;
3642
3643	time(&now);
3644	orig = pwent->pw_passwd;	/* save existing password */
3645	expire = pwent->pw_expire;
3646	change = (pwent->pw_change == _PASSWORD_CHGNOW)? now : pwent->pw_change;
3647
3648	if (orig[0] == '\0')		/* don't allow empty passwords */
3649		return 1;
3650
3651	new = crypt(password, orig);	/* encrypt given password */
3652	if (strcmp(new, orig) != 0)	/* compare */
3653		return 1;
3654
3655	if ((expire && now >= expire) || (change && now >= change))
3656		return 2;		/* check if expired */
3657
3658	return 0;			/* OK! */
3659}
3660
3661char *
3662ftpd_strdup(const char *s)
3663{
3664	char *new = strdup(s);
3665
3666	if (new == NULL)
3667		fatal("Local resource failure: malloc");
3668		/* NOTREACHED */
3669	return (new);
3670}
3671
3672/*
3673 * As per fprintf(), but increment total_bytes and total_bytes_out,
3674 * by the appropriate amount.
3675 */
3676void
3677cprintf(FILE *fd, const char *fmt, ...)
3678{
3679	off_t b;
3680	va_list ap;
3681
3682	va_start(ap, fmt);
3683	b = vfprintf(fd, fmt, ap);
3684	va_end(ap);
3685	total_bytes += b;
3686	total_bytes_out += b;
3687}
3688
3689#ifdef USE_PAM
3690/*
3691 * the following code is stolen from imap-uw PAM authentication module and
3692 * login.c
3693 */
3694#define COPY_STRING(s) (s ? strdup(s) : NULL)
3695
3696struct cred_t {
3697	const char *uname;		/* user name */
3698	const char *pass;		/* password */
3699};
3700typedef struct cred_t cred_t;
3701
3702static int
3703auth_conv(int num_msg, const struct pam_message **msg,
3704    struct pam_response **resp, void *appdata)
3705{
3706	int i;
3707	cred_t *cred = (cred_t *) appdata;
3708	struct pam_response *myreply;
3709
3710	myreply = calloc(num_msg, sizeof *myreply);
3711	if (myreply == NULL)
3712		return PAM_BUF_ERR;
3713
3714	for (i = 0; i < num_msg; i++) {
3715		switch (msg[i]->msg_style) {
3716		case PAM_PROMPT_ECHO_ON:	/* assume want user name */
3717			myreply[i].resp_retcode = PAM_SUCCESS;
3718			myreply[i].resp = COPY_STRING(cred->uname);
3719			/* PAM frees resp. */
3720			break;
3721		case PAM_PROMPT_ECHO_OFF:	/* assume want password */
3722			myreply[i].resp_retcode = PAM_SUCCESS;
3723			myreply[i].resp = COPY_STRING(cred->pass);
3724			/* PAM frees resp. */
3725			break;
3726		case PAM_TEXT_INFO:
3727		case PAM_ERROR_MSG:
3728			myreply[i].resp_retcode = PAM_SUCCESS;
3729			myreply[i].resp = NULL;
3730			break;
3731		default:			/* unknown message style */
3732			free(myreply);
3733			return PAM_CONV_ERR;
3734		}
3735	}
3736
3737	*resp = myreply;
3738	return PAM_SUCCESS;
3739}
3740
3741/*
3742 * Attempt to authenticate the user using PAM.  Returns 0 if the user is
3743 * authenticated, or 1 if not authenticated.  If some sort of PAM system
3744 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
3745 * function returns -1.  This can be used as an indication that we should
3746 * fall back to a different authentication mechanism.
3747 */
3748static int
3749auth_pam(struct passwd **ppw, const char *pwstr)
3750{
3751	const char *tmpl_user;
3752	const void *item;
3753	int rval;
3754	int e;
3755	cred_t auth_cred = { (*ppw)->pw_name, pwstr };
3756	struct pam_conv conv = { &auth_conv, &auth_cred };
3757
3758	e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh);
3759	if (e != PAM_SUCCESS) {
3760		/*
3761		 * In OpenPAM, it's OK to pass NULL to pam_strerror()
3762		 * if context creation has failed in the first place.
3763		 */
3764		syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e));
3765		return -1;
3766	}
3767
3768	e = pam_set_item(pamh, PAM_RHOST, remotehost);
3769	if (e != PAM_SUCCESS) {
3770		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
3771			pam_strerror(pamh, e));
3772		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3773			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3774		}
3775		pamh = NULL;
3776		return -1;
3777	}
3778
3779	e = pam_authenticate(pamh, 0);
3780	switch (e) {
3781	case PAM_SUCCESS:
3782		/*
3783		 * With PAM we support the concept of a "template"
3784		 * user.  The user enters a login name which is
3785		 * authenticated by PAM, usually via a remote service
3786		 * such as RADIUS or TACACS+.  If authentication
3787		 * succeeds, a different but related "template" name
3788		 * is used for setting the credentials, shell, and
3789		 * home directory.  The name the user enters need only
3790		 * exist on the remote authentication server, but the
3791		 * template name must be present in the local password
3792		 * database.
3793		 *
3794		 * This is supported by two various mechanisms in the
3795		 * individual modules.  However, from the application's
3796		 * point of view, the template user is always passed
3797		 * back as a changed value of the PAM_USER item.
3798		 */
3799		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
3800		    PAM_SUCCESS) {
3801			tmpl_user = (const char *) item;
3802			if (strcmp((*ppw)->pw_name, tmpl_user) != 0)
3803				*ppw = sgetpwnam(tmpl_user);
3804		} else
3805			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
3806			    pam_strerror(pamh, e));
3807		rval = 0;
3808		break;
3809
3810	case PAM_AUTH_ERR:
3811	case PAM_USER_UNKNOWN:
3812	case PAM_MAXTRIES:
3813		rval = 1;
3814		break;
3815
3816	default:
3817		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
3818		rval = -1;
3819		break;
3820	}
3821
3822	if (rval == 0) {
3823		e = pam_acct_mgmt(pamh, 0);
3824		if (e != PAM_SUCCESS) {
3825			syslog(LOG_ERR, "pam_acct_mgmt: %s",
3826						pam_strerror(pamh, e));
3827			rval = 1;
3828		}
3829	}
3830
3831	if (rval != 0) {
3832		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
3833			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
3834		}
3835		pamh = NULL;
3836	}
3837	return rval;
3838}
3839
3840#endif /* USE_PAM */
3841