ftpd.c revision 102474
1198090Srdivacky/*
2198090Srdivacky * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
3198090Srdivacky *	The Regents of the University of California.  All rights reserved.
4198090Srdivacky *
5198090Srdivacky * Redistribution and use in source and binary forms, with or without
6198090Srdivacky * modification, are permitted provided that the following conditions
7198090Srdivacky * are met:
8198090Srdivacky * 1. Redistributions of source code must retain the above copyright
9198090Srdivacky *    notice, this list of conditions and the following disclaimer.
10198090Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
11198090Srdivacky *    notice, this list of conditions and the following disclaimer in the
12198090Srdivacky *    documentation and/or other materials provided with the distribution.
13198090Srdivacky * 3. All advertising materials mentioning features or use of this software
14198090Srdivacky *    must display the following acknowledgement:
15198090Srdivacky *	This product includes software developed by the University of
16198090Srdivacky *	California, Berkeley and its contributors.
17198090Srdivacky * 4. Neither the name of the University nor the names of its contributors
18205218Srdivacky *    may be used to endorse or promote products derived from this software
19249423Sdim *    without specific prior written permission.
20198090Srdivacky *
21198090Srdivacky * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22198090Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23198090Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24263508Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25249423Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26198090Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27198090Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28198090Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30249423Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31249423Sdim * SUCH DAMAGE.
32198090Srdivacky */
33198090Srdivacky
34226633Sdim#if 0
35226633Sdim#ifndef lint
36226633Sdimstatic char copyright[] =
37226633Sdim"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
38226633Sdim	The Regents of the University of California.  All rights reserved.\n";
39226633Sdim#endif /* not lint */
40198090Srdivacky#endif
41218893Sdim
42198090Srdivacky#ifndef lint
43198090Srdivacky#if 0
44198090Srdivackystatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
45218893Sdim#endif
46198090Srdivackystatic const char rcsid[] =
47198090Srdivacky  "$FreeBSD: head/libexec/ftpd/ftpd.c 102474 2002-08-27 09:05:03Z yar $";
48198090Srdivacky#endif /* not lint */
49198090Srdivacky
50198090Srdivacky/*
51198090Srdivacky * FTP server.
52239462Sdim */
53218893Sdim#include <sys/param.h>
54198090Srdivacky#include <sys/ioctl.h>
55198090Srdivacky#include <sys/mman.h>
56239462Sdim#include <sys/socket.h>
57239462Sdim#include <sys/stat.h>
58239462Sdim#include <sys/time.h>
59243830Sdim#include <sys/wait.h>
60243830Sdim
61198090Srdivacky#include <netinet/in.h>
62218893Sdim#include <netinet/in_systm.h>
63198090Srdivacky#include <netinet/ip.h>
64198090Srdivacky#include <netinet/tcp.h>
65198090Srdivacky
66239462Sdim#define	FTP_NAMES
67239462Sdim#include <arpa/ftp.h>
68243830Sdim#include <arpa/inet.h>
69243830Sdim#include <arpa/telnet.h>
70239462Sdim
71239462Sdim#include <ctype.h>
72239462Sdim#include <dirent.h>
73239462Sdim#include <err.h>
74239462Sdim#include <errno.h>
75239462Sdim#include <fcntl.h>
76239462Sdim#include <glob.h>
77239462Sdim#include <limits.h>
78239462Sdim#include <netdb.h>
79198090Srdivacky#include <pwd.h>
80198090Srdivacky#include <grp.h>
81198090Srdivacky#include <opie.h>
82198090Srdivacky#include <signal.h>
83198090Srdivacky#include <stdio.h>
84198090Srdivacky#include <stdlib.h>
85198090Srdivacky#include <string.h>
86198090Srdivacky#include <syslog.h>
87198090Srdivacky#include <time.h>
88198090Srdivacky#include <unistd.h>
89198090Srdivacky#include <libutil.h>
90198090Srdivacky#ifdef	LOGIN_CAP
91198090Srdivacky#include <login_cap.h>
92198090Srdivacky#endif
93198090Srdivacky
94218893Sdim#ifdef USE_PAM
95198090Srdivacky#include <security/pam_appl.h>
96198090Srdivacky#endif
97198090Srdivacky
98198090Srdivacky#include "pathnames.h"
99198090Srdivacky#include "extern.h"
100198090Srdivacky
101198090Srdivacky#include <stdarg.h>
102198090Srdivacky
103249423Sdimstatic char version[] = "Version 6.00LS";
104224145Sdim#undef main
105224145Sdim
106224145Sdimextern	off_t restart_point;
107224145Sdimextern	char cbuf[];
108224145Sdim
109198090Srdivackyunion sockunion server_addr;
110198090Srdivackyunion sockunion ctrl_addr;
111198090Srdivackyunion sockunion data_source;
112198090Srdivackyunion sockunion data_dest;
113198090Srdivackyunion sockunion his_addr;
114226633Sdimunion sockunion pasv_addr;
115198090Srdivacky
116198090Srdivackyint	daemon_mode;
117198090Srdivackyint	data;
118198090Srdivackyint	logged_in;
119198090Srdivackystruct	passwd *pw;
120198090Srdivackyint	ftpdebug;
121198090Srdivackyint	timeout = 900;    /* timeout after 15 minutes of inactivity */
122198090Srdivackyint	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
123198090Srdivackyint	logging;
124198090Srdivackyint	restricted_data_ports = 1;
125198090Srdivackyint	paranoid = 1;	  /* be extra careful about security */
126198090Srdivackyint	anon_only = 0;    /* Only anonymous ftp allowed */
127198090Srdivackyint	guest;
128249423Sdimint	dochroot;
129218893Sdimint	dowtmp = 1;
130198090Srdivackyint	stats;
131198090Srdivackyint	statfd = -1;
132198090Srdivackyint	type;
133224145Sdimint	form;
134198090Srdivackyint	stru;			/* avoid C keyword */
135239462Sdimint	mode;
136239462Sdimint	usedefault = 1;		/* for data transfers */
137239462Sdimint	pdata = -1;		/* for passive mode */
138239462Sdimint	readonly=0;		/* Server is in readonly mode.	*/
139198090Srdivackyint	noepsv=0;		/* EPSV command is disabled.	*/
140198090Srdivackyint	noretr=0;		/* RETR command is disabled.	*/
141198090Srdivackyint	noguestretr=0;		/* RETR command is disabled for anon users. */
142226633Sdimint	noguestmkd=0;		/* MKD command is disabled for anon users. */
143226633Sdimint	noguestmod=1;		/* anon users may not modify existing files. */
144198090Srdivacky
145198090Srdivackystatic volatile sig_atomic_t recvurg;
146198090Srdivackysig_atomic_t transflag;
147198090Srdivackyoff_t	file_size;
148198090Srdivackyoff_t	byte_count;
149198090Srdivacky#if !defined(CMASK) || CMASK == 0
150198090Srdivacky#undef CMASK
151198090Srdivacky#define CMASK 027
152198090Srdivacky#endif
153198090Srdivackyint	defumask = CMASK;		/* default umask value */
154198090Srdivackychar	tmpline[7];
155198090Srdivackychar	*hostname;
156198090Srdivackyint	epsvall = 0;
157198090Srdivacky
158210299Sed#ifdef VIRTUAL_HOSTING
159224145Sdimchar	*ftpuser;
160198090Srdivacky
161198090Srdivackystatic struct ftphost {
162198090Srdivacky	struct ftphost	*next;
163198090Srdivacky	struct addrinfo *hostinfo;
164198090Srdivacky	char		*hostname;
165198090Srdivacky	char		*anonuser;
166198090Srdivacky	char		*statfile;
167198090Srdivacky	char		*welcome;
168198090Srdivacky	char		*loginmsg;
169226633Sdim} *thishost, *firsthost;
170198090Srdivacky
171198090Srdivacky#endif
172198090Srdivackychar	remotehost[MAXHOSTNAMELEN];
173198090Srdivackychar	*ident = NULL;
174198090Srdivacky
175198090Srdivackystatic char ttyline[20];
176198090Srdivackychar	*tty = ttyline;		/* for klogin */
177210299Sed
178210299Sed#ifdef USE_PAM
179198090Srdivackystatic int	auth_pam(struct passwd**, const char*);
180198090Srdivackypam_handle_t *pamh = NULL;
181198090Srdivacky#endif
182198090Srdivacky
183198090Srdivackystatic struct opie opiedata;
184198090Srdivackystatic char opieprompt[OPIE_CHALLENGE_MAX+1];
185218893Sdimstatic int pwok;
186198090Srdivacky
187198090Srdivackychar	*pid_file = NULL;
188198090Srdivacky
189198090Srdivacky/*
190198090Srdivacky * Limit number of pathnames that glob can return.
191198090Srdivacky * A limit of 0 indicates the number of pathnames is unlimited.
192198090Srdivacky */
193198090Srdivacky#define MAXGLOBARGS	16384
194198090Srdivacky#
195198090Srdivacky
196198090Srdivacky/*
197226633Sdim * Timeout intervals for retrying connections
198198090Srdivacky * to hosts that don't accept PORT cmds.  This
199198090Srdivacky * is a kludge, but given the problems with TCP...
200198090Srdivacky */
201198090Srdivacky#define	SWAITMAX	90	/* wait at most 90 seconds */
202198090Srdivacky#define	SWAITINT	5	/* interval between retries */
203198090Srdivacky
204198090Srdivackyint	swaitmax = SWAITMAX;
205198090Srdivackyint	swaitint = SWAITINT;
206198090Srdivacky
207249423Sdim#ifdef SETPROCTITLE
208249423Sdim#ifdef OLD_SETPROCTITLE
209224145Sdimchar	**Argv = NULL;		/* pointer to argument vector */
210198090Srdivackychar	*LastArgv = NULL;	/* end of argv */
211198090Srdivacky#endif /* OLD_SETPROCTITLE */
212203954Srdivackychar	proctitle[LINE_MAX];	/* initial part of title */
213198090Srdivacky#endif /* SETPROCTITLE */
214198090Srdivacky
215263508Sdim#define LOGCMD(cmd, file) \
216198090Srdivacky	if (logging > 1) \
217198090Srdivacky	    syslog(LOG_INFO,"%s %s%s", cmd, \
218198090Srdivacky		*(file) == '/' ? "" : curdir(), file);
219198090Srdivacky#define LOGCMD2(cmd, file1, file2) \
220198090Srdivacky	 if (logging > 1) \
221239462Sdim	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
222239462Sdim		*(file1) == '/' ? "" : curdir(), file1, \
223263763Sdim		*(file2) == '/' ? "" : curdir(), file2);
224263763Sdim#define LOGBYTES(cmd, file, cnt) \
225263763Sdim	if (logging > 1) { \
226263763Sdim		if (cnt == (off_t)-1) \
227263763Sdim		    syslog(LOG_INFO,"%s %s%s", cmd, \
228263763Sdim			*(file) == '/' ? "" : curdir(), file); \
229263763Sdim		else \
230263763Sdim		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
231263763Sdim			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
232263763Sdim	}
233263763Sdim
234263763Sdim#ifdef VIRTUAL_HOSTING
235263763Sdimstatic void	 inithosts(void);
236198090Srdivackystatic void	selecthost(union sockunion *);
237198090Srdivacky#endif
238198090Srdivackystatic void	 ack(char *);
239198090Srdivackystatic void	 sigurg(int);
240198090Srdivackystatic void	 myoob(void);
241249423Sdimstatic int	 checkuser(char *, char *, int);
242198090Srdivackystatic FILE	*dataconn(char *, off_t, char *);
243198090Srdivackystatic void	 dolog(struct sockaddr *);
244198090Srdivackystatic char	*curdir(void);
245198090Srdivackystatic void	 end_login(void);
246198090Srdivackystatic FILE	*getdatasock(char *);
247198090Srdivackystatic int	 guniquefd(char *, char **);
248226633Sdimstatic void	 lostconn(int);
249198090Srdivackystatic void	 sigquit(int);
250198090Srdivackystatic int	 receive_data(FILE *, FILE *);
251198090Srdivackystatic int	 send_data(FILE *, FILE *, off_t, off_t, int);
252198090Srdivackystatic struct passwd *
253198090Srdivacky		 sgetpwnam(char *);
254198090Srdivackystatic char	*sgetsave(char *);
255198090Srdivackystatic void	 reapchild(int);
256249423Sdimstatic void      logxfer(char *, off_t, time_t);
257198090Srdivackystatic char	*doublequote(char *);
258198090Srdivacky
259198090Srdivackystatic char *
260198090Srdivackycurdir(void)
261198090Srdivacky{
262198090Srdivacky	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
263198090Srdivacky
264198090Srdivacky	if (getcwd(path, sizeof(path)-2) == NULL)
265198090Srdivacky		return ("");
266198090Srdivacky	if (path[1] != '\0')		/* special case for root dir. */
267198090Srdivacky		strcat(path, "/");
268249423Sdim	/* For guest account, skip / since it's chrooted */
269198090Srdivacky	return (guest ? path+1 : path);
270198090Srdivacky}
271198090Srdivacky
272198090Srdivackyint
273198090Srdivackymain(int argc, char *argv[], char **envp)
274198090Srdivacky{
275218893Sdim	int addrlen, ch, on = 1, tos;
276198090Srdivacky	char *cp, line[LINE_MAX];
277198090Srdivacky	FILE *fd;
278198090Srdivacky	int error;
279198090Srdivacky	char	*bindname = NULL;
280198090Srdivacky	int	family = AF_UNSPEC;
281198090Srdivacky	int	enable_v4 = 0;
282198090Srdivacky	struct sigaction sa;
283198090Srdivacky
284198090Srdivacky	tzset();		/* in case no timezone database in ~ftp */
285203954Srdivacky	sigemptyset(&sa.sa_mask);
286198090Srdivacky	sa.sa_flags = SA_RESTART;
287198090Srdivacky
288224145Sdim#ifdef OLD_SETPROCTITLE
289198090Srdivacky	/*
290198090Srdivacky	 *  Save start and extent of argv for setproctitle.
291249423Sdim	 */
292249423Sdim	Argv = argv;
293198090Srdivacky	while (*envp)
294198090Srdivacky		envp++;
295210299Sed	LastArgv = envp[-1] + strlen(envp[-1]);
296203954Srdivacky#endif /* OLD_SETPROCTITLE */
297198090Srdivacky
298198090Srdivacky
299198090Srdivacky	while ((ch = getopt(argc, argv, "46a:AdDElmMoOp:rRSt:T:u:UvW")) != -1) {
300198090Srdivacky		switch (ch) {
301198090Srdivacky		case '4':
302198090Srdivacky			enable_v4 = 1;
303198090Srdivacky			if (family == AF_UNSPEC)
304198090Srdivacky				family = AF_INET;
305198090Srdivacky			break;
306198090Srdivacky
307198090Srdivacky		case '6':
308198090Srdivacky			family = AF_INET6;
309198090Srdivacky			break;
310249423Sdim
311249423Sdim		case 'a':
312198090Srdivacky			bindname = optarg;
313224145Sdim			break;
314206083Srdivacky
315208599Srdivacky		case 'A':
316198090Srdivacky			anon_only = 1;
317218893Sdim			break;
318218893Sdim
319198090Srdivacky		case 'd':
320198090Srdivacky			ftpdebug++;
321198090Srdivacky			break;
322198090Srdivacky
323249423Sdim		case 'D':
324224145Sdim			daemon_mode++;
325224145Sdim			break;
326198090Srdivacky
327198090Srdivacky		case 'E':
328226633Sdim			noepsv = 1;
329226633Sdim			break;
330226633Sdim
331198090Srdivacky		case 'l':
332198090Srdivacky			logging++;	/* > 1 == extra logging */
333198090Srdivacky			break;
334239462Sdim
335226633Sdim		case 'm':
336198090Srdivacky			noguestmod = 0;
337210299Sed			break;
338210299Sed
339198090Srdivacky		case 'M':
340198090Srdivacky			noguestmkd = 1;
341198090Srdivacky			break;
342198090Srdivacky
343207618Srdivacky		case 'o':
344208599Srdivacky			noretr = 1;
345208599Srdivacky			break;
346208599Srdivacky
347208599Srdivacky		case 'O':
348208599Srdivacky			noguestretr = 1;
349208599Srdivacky			break;
350208599Srdivacky
351208599Srdivacky		case 'p':
352208599Srdivacky			pid_file = optarg;
353208599Srdivacky			break;
354208599Srdivacky
355249423Sdim		case 'r':
356208599Srdivacky			readonly = 1;
357249423Sdim			break;
358249423Sdim
359208599Srdivacky		case 'R':
360249423Sdim			paranoid = 0;
361208599Srdivacky			break;
362208599Srdivacky
363208599Srdivacky		case 'S':
364207618Srdivacky			stats++;
365249423Sdim			break;
366249423Sdim
367198090Srdivacky		case 't':
368198090Srdivacky			timeout = atoi(optarg);
369198090Srdivacky			if (maxtimeout < timeout)
370198090Srdivacky				maxtimeout = timeout;
371239462Sdim			break;
372249423Sdim
373249423Sdim		case 'T':
374198090Srdivacky			maxtimeout = atoi(optarg);
375224145Sdim			if (timeout > maxtimeout)
376206083Srdivacky				timeout = maxtimeout;
377208599Srdivacky			break;
378198090Srdivacky
379249423Sdim		case 'u':
380208599Srdivacky		    {
381198090Srdivacky			long val = 0;
382249423Sdim
383198090Srdivacky			val = strtol(optarg, &optarg, 8);
384249423Sdim			if (*optarg != '\0' || val < 0)
385198090Srdivacky				warnx("bad value for -u");
386239462Sdim			else
387239462Sdim				defumask = val;
388239462Sdim			break;
389239462Sdim		    }
390249423Sdim		case 'U':
391234353Sdim			restricted_data_ports = 0;
392249423Sdim			break;
393198090Srdivacky
394249423Sdim		case 'v':
395249423Sdim			ftpdebug++;
396198090Srdivacky			break;
397249423Sdim
398198090Srdivacky		case 'W':
399249423Sdim			dowtmp = 0;
400198090Srdivacky			break;
401249423Sdim
402198090Srdivacky		default:
403198090Srdivacky			warnx("unknown flag -%c ignored", optopt);
404198090Srdivacky			break;
405226633Sdim		}
406198090Srdivacky	}
407198090Srdivacky
408243830Sdim#ifdef VIRTUAL_HOSTING
409198090Srdivacky	inithosts();
410198090Srdivacky#endif
411243830Sdim	(void) freopen(_PATH_DEVNULL, "w", stderr);
412198090Srdivacky
413198090Srdivacky	/*
414226633Sdim	 * LOG_NDELAY sets up the logging connection immediately,
415198090Srdivacky	 * necessary for anonymous ftp's that chroot and can't do it later.
416198090Srdivacky	 */
417198090Srdivacky	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
418198090Srdivacky
419198090Srdivacky	if (daemon_mode) {
420198090Srdivacky		int ctl_sock, fd;
421249423Sdim		struct addrinfo hints, *res;
422198090Srdivacky
423249423Sdim		/*
424198892Srdivacky		 * Detach from parent.
425249423Sdim		 */
426249423Sdim		if (daemon(1, 1) < 0) {
427249423Sdim			syslog(LOG_ERR, "failed to become a daemon");
428239462Sdim			exit(1);
429249423Sdim		}
430198090Srdivacky		sa.sa_handler = reapchild;
431198090Srdivacky		(void)sigaction(SIGCHLD, &sa, NULL);
432218893Sdim		/* init bind_sa */
433218893Sdim		memset(&hints, 0, sizeof(hints));
434249423Sdim
435208599Srdivacky		hints.ai_family = family == AF_UNSPEC ? AF_INET : family;
436198090Srdivacky		hints.ai_socktype = SOCK_STREAM;
437198090Srdivacky		hints.ai_protocol = 0;
438198090Srdivacky		hints.ai_flags = AI_PASSIVE;
439226633Sdim		error = getaddrinfo(bindname, "ftp", &hints, &res);
440249423Sdim		if (error) {
441226633Sdim			if (family == AF_UNSPEC) {
442226633Sdim				hints.ai_family = AF_UNSPEC;
443226633Sdim				error = getaddrinfo(bindname, "ftp", &hints,
444226633Sdim						    &res);
445226633Sdim			}
446226633Sdim		}
447226633Sdim		if (error) {
448226633Sdim			syslog(LOG_ERR, "%s", gai_strerror(error));
449226633Sdim			if (error == EAI_SYSTEM)
450226633Sdim				syslog(LOG_ERR, "%s", strerror(errno));
451226633Sdim			exit(1);
452226633Sdim		}
453226633Sdim		if (res->ai_addr == NULL) {
454226633Sdim			syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
455226633Sdim			exit(1);
456226633Sdim		} else
457226633Sdim			family = res->ai_addr->sa_family;
458226633Sdim		/*
459226633Sdim		 * Open a socket, bind it to the FTP port, and start
460226633Sdim		 * listening.
461198090Srdivacky		 */
462198090Srdivacky		ctl_sock = socket(family, SOCK_STREAM, 0);
463198090Srdivacky		if (ctl_sock < 0) {
464198090Srdivacky			syslog(LOG_ERR, "control socket: %m");
465226633Sdim			exit(1);
466208599Srdivacky		}
467208599Srdivacky		if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
468198090Srdivacky		    &on, sizeof(on)) < 0)
469198090Srdivacky			syslog(LOG_WARNING,
470226633Sdim			       "control setsockopt (SO_REUSEADDR): %m");
471198090Srdivacky		if (family == AF_INET6 && enable_v4 == 0) {
472198090Srdivacky			if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_V6ONLY,
473198090Srdivacky				       &on, sizeof (on)) < 0)
474198090Srdivacky				syslog(LOG_WARNING,
475198090Srdivacky				       "control setsockopt (IPV6_V6ONLY): %m");
476226633Sdim		}
477198090Srdivacky		memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len);
478198090Srdivacky		if (bind(ctl_sock, (struct sockaddr *)&server_addr,
479198090Srdivacky			 server_addr.su_len) < 0) {
480198090Srdivacky			syslog(LOG_ERR, "control bind: %m");
481198090Srdivacky			exit(1);
482198090Srdivacky		}
483198090Srdivacky		if (listen(ctl_sock, 32) < 0) {
484198090Srdivacky			syslog(LOG_ERR, "control listen: %m");
485226633Sdim			exit(1);
486203954Srdivacky		}
487226633Sdim		/*
488226633Sdim		 * Atomically write process ID
489226633Sdim		 */
490198090Srdivacky		if (pid_file)
491249423Sdim		{
492249423Sdim			int fd;
493198090Srdivacky			char buf[20];
494198090Srdivacky
495218893Sdim			fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC
496218893Sdim				| O_NONBLOCK | O_EXLOCK, 0644);
497218893Sdim			if (fd < 0) {
498218893Sdim				if (errno == EAGAIN)
499239462Sdim					errx(1, "%s: file locked", pid_file);
500239462Sdim				else
501218893Sdim					err(1, "%s", pid_file);
502218893Sdim			}
503218893Sdim			snprintf(buf, sizeof(buf),
504218893Sdim				"%lu\n", (unsigned long) getpid());
505218893Sdim			if (write(fd, buf, strlen(buf)) < 0)
506218893Sdim				err(1, "%s: write", pid_file);
507218893Sdim			/* Leave the pid file open and locked */
508218893Sdim		}
509239462Sdim		/*
510218893Sdim		 * Loop forever accepting connection requests and forking off
511226633Sdim		 * children to handle them.
512226633Sdim		 */
513226633Sdim		while (1) {
514226633Sdim			addrlen = server_addr.su_len;
515249423Sdim			fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
516226633Sdim			if (fork() == 0) {
517198090Srdivacky				/* child */
518226633Sdim				(void) dup2(fd, 0);
519226633Sdim				(void) dup2(fd, 1);
520226633Sdim				close(ctl_sock);
521198090Srdivacky				break;
522218893Sdim			}
523226633Sdim			close(fd);
524226633Sdim		}
525218893Sdim	} else {
526203954Srdivacky		addrlen = sizeof(his_addr);
527203954Srdivacky		if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
528198090Srdivacky			syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
529198090Srdivacky			exit(1);
530198090Srdivacky		}
531198090Srdivacky	}
532198090Srdivacky
533226633Sdim	sa.sa_handler = SIG_DFL;
534226633Sdim	(void)sigaction(SIGCHLD, &sa, NULL);
535226633Sdim
536226633Sdim	sa.sa_handler = sigurg;
537226633Sdim	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
538226633Sdim	(void)sigaction(SIGURG, &sa, NULL);
539226633Sdim
540226633Sdim	sigfillset(&sa.sa_mask);	/* block all signals in handler */
541226633Sdim	sa.sa_flags = SA_RESTART;
542226633Sdim	sa.sa_handler = sigquit;
543226633Sdim	(void)sigaction(SIGHUP, &sa, NULL);
544226633Sdim	(void)sigaction(SIGINT, &sa, NULL);
545226633Sdim	(void)sigaction(SIGQUIT, &sa, NULL);
546226633Sdim	(void)sigaction(SIGTERM, &sa, NULL);
547249423Sdim
548226633Sdim	sa.sa_handler = lostconn;
549226633Sdim	(void)sigaction(SIGPIPE, &sa, NULL);
550226633Sdim
551226633Sdim	addrlen = sizeof(ctrl_addr);
552198090Srdivacky	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
553198090Srdivacky		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
554198090Srdivacky		exit(1);
555249423Sdim	}
556249423Sdim#ifdef VIRTUAL_HOSTING
557226633Sdim	/* select our identity from virtual host table */
558198090Srdivacky	selecthost(&ctrl_addr);
559198090Srdivacky#endif
560203954Srdivacky#ifdef IP_TOS
561198090Srdivacky	if (ctrl_addr.su_family == AF_INET)
562249423Sdim      {
563198090Srdivacky	tos = IPTOS_LOWDELAY;
564249423Sdim	if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
565208599Srdivacky		syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m");
566198090Srdivacky      }
567249423Sdim#endif
568208599Srdivacky	/*
569249423Sdim	 * Disable Nagle on the control channel so that we don't have to wait
570249423Sdim	 * for peer's ACK before issuing our next reply.
571198090Srdivacky	 */
572198090Srdivacky	if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
573226633Sdim		syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m");
574198090Srdivacky
575198090Srdivacky	data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
576218893Sdim
577198090Srdivacky	/* set this here so klogin can use it... */
578198090Srdivacky	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
579198090Srdivacky
580198090Srdivacky	/* Try to handle urgent data inline */
581198090Srdivacky#ifdef SO_OOBINLINE
582198090Srdivacky	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0)
583198090Srdivacky		syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m");
584198090Srdivacky#endif
585198090Srdivacky
586198090Srdivacky#ifdef	F_SETOWN
587198090Srdivacky	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
588198090Srdivacky		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
589210299Sed#endif
590198090Srdivacky	dolog((struct sockaddr *)&his_addr);
591239462Sdim	/*
592239462Sdim	 * Set up default state
593198090Srdivacky	 */
594210299Sed	data = -1;
595210299Sed	type = TYPE_A;
596198090Srdivacky	form = FORM_N;
597198090Srdivacky	stru = STRU_F;
598198090Srdivacky	mode = MODE_S;
599218893Sdim	tmpline[0] = '\0';
600198090Srdivacky
601198090Srdivacky	/* If logins are disabled, print out the message. */
602198090Srdivacky	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
603207618Srdivacky		while (fgets(line, sizeof(line), fd) != NULL) {
604207618Srdivacky			if ((cp = strchr(line, '\n')) != NULL)
605207618Srdivacky				*cp = '\0';
606208599Srdivacky			lreply(530, "%s", line);
607208599Srdivacky		}
608224145Sdim		(void) fflush(stdout);
609224145Sdim		(void) fclose(fd);
610239462Sdim		reply(530, "System not available.");
611249423Sdim		exit(0);
612249423Sdim	}
613207618Srdivacky#ifdef VIRTUAL_HOSTING
614224145Sdim	if ((fd = fopen(thishost->welcome, "r")) != NULL) {
615224145Sdim#else
616224145Sdim	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
617207618Srdivacky#endif
618224145Sdim		while (fgets(line, sizeof(line), fd) != NULL) {
619234353Sdim			if ((cp = strchr(line, '\n')) != NULL)
620234353Sdim				*cp = '\0';
621234353Sdim			lreply(220, "%s", line);
622234353Sdim		}
623234353Sdim		(void) fflush(stdout);
624234353Sdim		(void) fclose(fd);
625234353Sdim		/* reply(220,) must follow */
626234353Sdim	}
627208599Srdivacky#ifndef VIRTUAL_HOSTING
628234353Sdim	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
629234353Sdim		fatalerror("Ran out of memory.");
630234353Sdim	(void) gethostname(hostname, MAXHOSTNAMELEN - 1);
631234353Sdim	hostname[MAXHOSTNAMELEN - 1] = '\0';
632208599Srdivacky#endif
633207618Srdivacky	reply(220, "%s FTP server (%s) ready.", hostname, version);
634249423Sdim	for (;;)
635208599Srdivacky		(void) yyparse();
636207618Srdivacky	/* NOTREACHED */
637207618Srdivacky}
638249423Sdim
639207618Srdivackystatic void
640207618Srdivackylostconn(int signo)
641218893Sdim{
642207618Srdivacky
643207618Srdivacky	if (ftpdebug)
644207618Srdivacky		syslog(LOG_DEBUG, "lost connection");
645206083Srdivacky	dologout(1);
646206083Srdivacky}
647207618Srdivacky
648207618Srdivackystatic void
649207618Srdivackysigquit(int signo)
650206083Srdivacky{
651206083Srdivacky
652206083Srdivacky	syslog(LOG_ERR, "got signal %d", signo);
653204792Srdivacky	dologout(1);
654207618Srdivacky}
655207618Srdivacky
656207618Srdivacky#ifdef VIRTUAL_HOSTING
657263508Sdim/*
658263508Sdim * read in virtual host tables (if they exist)
659207618Srdivacky */
660207618Srdivacky
661224145Sdimstatic void
662206083Srdivackyinithosts(void)
663206083Srdivacky{
664207618Srdivacky	int insert;
665207618Srdivacky	size_t len;
666207618Srdivacky	FILE *fp;
667207618Srdivacky	char *cp, *mp, *line;
668207618Srdivacky	char *hostname;
669207618Srdivacky	char *vhost, *anonuser, *statfile, *welcome, *loginmsg;
670207618Srdivacky	struct ftphost *hrp, *lhrp;
671207618Srdivacky	struct addrinfo hints, *res, *ai;
672207618Srdivacky
673207618Srdivacky	/*
674207618Srdivacky	 * Fill in the default host information
675249423Sdim	 */
676208599Srdivacky	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
677206083Srdivacky		fatalerror("Ran out of memory.");
678207618Srdivacky	if (gethostname(hostname, MAXHOSTNAMELEN) < 0)
679207618Srdivacky		hostname[0] = '\0';
680224145Sdim	hostname[MAXHOSTNAMELEN - 1] = '\0';
681224145Sdim	if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
682208599Srdivacky		fatalerror("Ran out of memory.");
683208599Srdivacky	hrp->hostname = hostname;
684207618Srdivacky	hrp->hostinfo = NULL;
685206083Srdivacky
686205218Srdivacky	memset(&hints, 0, sizeof(hints));
687205218Srdivacky	hints.ai_flags = AI_CANONNAME;
688205218Srdivacky	hints.ai_family = AF_UNSPEC;
689206083Srdivacky	if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0)
690205218Srdivacky		hrp->hostinfo = res;
691204792Srdivacky	hrp->statfile = _PATH_FTPDSTATFILE;
692204792Srdivacky	hrp->welcome  = _PATH_FTPWELCOME;
693206083Srdivacky	hrp->loginmsg = _PATH_FTPLOGINMESG;
694204792Srdivacky	hrp->anonuser = "ftp";
695206083Srdivacky	hrp->next = NULL;
696263508Sdim	thishost = firsthost = lhrp = hrp;
697263508Sdim	if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
698263508Sdim		int addrsize, gothost;
699263508Sdim		void *addr;
700263508Sdim		struct hostent *hp;
701263508Sdim
702263508Sdim		while ((line = fgetln(fp, &len)) != NULL) {
703206083Srdivacky			int	i, hp_error;
704204792Srdivacky
705204792Srdivacky			/* skip comments */
706206083Srdivacky			if (line[0] == '#')
707206083Srdivacky				continue;
708198090Srdivacky			if (line[len - 1] == '\n') {
709206083Srdivacky				line[len - 1] = '\0';
710206083Srdivacky				mp = NULL;
711207618Srdivacky			} else {
712206083Srdivacky				if ((mp = malloc(len + 1)) == NULL)
713226633Sdim					fatalerror("Ran out of memory.");
714206083Srdivacky				memcpy(mp, line, len);
715226633Sdim				mp[len] = '\0';
716206083Srdivacky				line = mp;
717206083Srdivacky			}
718208599Srdivacky			cp = strtok(line, " \t");
719206083Srdivacky			/* skip empty lines */
720206083Srdivacky			if (cp == NULL)
721198090Srdivacky				goto nextline;
722206083Srdivacky			vhost = cp;
723206083Srdivacky
724206083Srdivacky			/* set defaults */
725206083Srdivacky			anonuser = "ftp";
726206083Srdivacky			statfile = _PATH_FTPDSTATFILE;
727198090Srdivacky			welcome  = _PATH_FTPWELCOME;
728207618Srdivacky			loginmsg = _PATH_FTPLOGINMESG;
729207618Srdivacky
730208599Srdivacky			/*
731207618Srdivacky			 * Preparse the line so we can use its info
732207618Srdivacky			 * for all the addresses associated with
733207618Srdivacky			 * the virtual host name.
734206083Srdivacky			 * Field 0, the virtual host name, is special:
735206083Srdivacky			 * it's already parsed off and will be strdup'ed
736206083Srdivacky			 * later, after we know its canonical form.
737226633Sdim			 */
738224145Sdim			for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++)
739206083Srdivacky				if (*cp != '-' && (cp = strdup(cp)))
740263508Sdim					switch (i) {
741263508Sdim					case 1:	/* anon user permissions */
742263508Sdim						anonuser = cp;
743263508Sdim						break;
744263508Sdim					case 2: /* statistics file */
745263508Sdim						statfile = cp;
746263508Sdim						break;
747263508Sdim					case 3: /* welcome message */
748263508Sdim						welcome  = cp;
749263508Sdim						break;
750239462Sdim					case 4: /* login message */
751243830Sdim						loginmsg = cp;
752263508Sdim						break;
753263508Sdim					default: /* programming error */
754198090Srdivacky						abort();
755206083Srdivacky						/* NOTREACHED */
756206083Srdivacky					}
757206083Srdivacky
758206083Srdivacky			hints.ai_flags = 0;
759206083Srdivacky			hints.ai_family = AF_UNSPEC;
760206083Srdivacky			hints.ai_flags = AI_PASSIVE;
761239462Sdim			if (getaddrinfo(vhost, NULL, &hints, &res) != 0)
762239462Sdim				goto nextline;
763206083Srdivacky			for (ai = res; ai != NULL && ai->ai_addr != NULL;
764198090Srdivacky			     ai = ai->ai_next) {
765198090Srdivacky
766206083Srdivacky			gothost = 0;
767249423Sdim			for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
768210299Sed				struct addrinfo *hi;
769206083Srdivacky
770206083Srdivacky				for (hi = hrp->hostinfo; hi != NULL;
771206083Srdivacky				     hi = hi->ai_next)
772249423Sdim					if (hi->ai_addrlen == ai->ai_addrlen &&
773226633Sdim					    memcmp(hi->ai_addr,
774206083Srdivacky						   ai->ai_addr,
775206083Srdivacky						   ai->ai_addr->sa_len) == 0) {
776263508Sdim						gothost++;
777206083Srdivacky						break;
778206083Srdivacky					}
779263508Sdim				if (gothost)
780206083Srdivacky					break;
781263508Sdim			}
782208599Srdivacky			if (hrp == NULL) {
783198090Srdivacky				if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
784263508Sdim					goto nextline;
785263508Sdim				hrp->hostname = NULL;
786263508Sdim				insert = 1;
787263508Sdim			} else {
788263508Sdim				if (hrp->hostinfo)
789263508Sdim					freeaddrinfo(hrp->hostinfo);
790206083Srdivacky				insert = 0; /* host already in the chain */
791249423Sdim			}
792206083Srdivacky			hrp->hostinfo = res;
793198090Srdivacky
794210299Sed			/*
795210299Sed			 * determine hostname to use.
796210299Sed			 * force defined name if there is a valid alias
797249423Sdim			 * otherwise fallback to primary hostname
798210299Sed			 */
799234353Sdim			/* XXX: getaddrinfo() can't do alias check */
800234353Sdim			switch(hrp->hostinfo->ai_family) {
801234353Sdim			case AF_INET:
802234353Sdim				addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr;
803234353Sdim				addrsize = sizeof(struct in_addr);
804234353Sdim				break;
805234353Sdim			case AF_INET6:
806234353Sdim				addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr;
807234353Sdim				addrsize = sizeof(struct in6_addr);
808234353Sdim				break;
809234353Sdim			default:
810234353Sdim				/* should not reach here */
811234353Sdim				freeaddrinfo(hrp->hostinfo);
812234353Sdim				if (insert)
813234353Sdim					free(hrp); /*not in chain, can free*/
814234353Sdim				else
815234353Sdim					hrp->hostinfo = NULL; /*mark as blank*/
816218893Sdim				goto nextline;
817206083Srdivacky				/* NOTREACHED */
818263508Sdim			}
819263508Sdim			if ((hp = getipnodebyaddr(addr, addrsize,
820234353Sdim						  hrp->hostinfo->ai_family,
821234353Sdim						  &hp_error)) != NULL) {
822234353Sdim				if (strcmp(vhost, hp->h_name) != 0) {
823234353Sdim					if (hp->h_aliases == NULL)
824234353Sdim						vhost = hp->h_name;
825198090Srdivacky					else {
826198090Srdivacky						i = 0;
827226633Sdim						while (hp->h_aliases[i] &&
828234353Sdim						       strcmp(vhost, hp->h_aliases[i]) != 0)
829234353Sdim							++i;
830234353Sdim						if (hp->h_aliases[i] == NULL)
831234353Sdim							vhost = hp->h_name;
832234353Sdim					}
833234353Sdim				}
834234353Sdim			}
835234353Sdim			if (hrp->hostname &&
836234353Sdim			    strcmp(hrp->hostname, vhost) != 0) {
837234353Sdim				free(hrp->hostname);
838234353Sdim				hrp->hostname = NULL;
839234353Sdim			}
840234353Sdim			if (hrp->hostname == NULL &&
841234353Sdim			    (hrp->hostname = strdup(vhost)) == NULL) {
842234353Sdim				freeaddrinfo(hrp->hostinfo);
843234353Sdim				hrp->hostinfo = NULL; /* mark as blank */
844234353Sdim				if (hp)
845234353Sdim					freehostent(hp);
846234353Sdim				goto nextline;
847234353Sdim			}
848234353Sdim			hrp->anonuser = anonuser;
849234353Sdim			hrp->statfile = statfile;
850206083Srdivacky			hrp->welcome  = welcome;
851234353Sdim			hrp->loginmsg = loginmsg;
852226633Sdim			if (insert) {
853234353Sdim				hrp->next  = NULL;
854234353Sdim				lhrp->next = hrp;
855249423Sdim				lhrp = hrp;
856234353Sdim			}
857226633Sdim			if (hp)
858226633Sdim				freehostent(hp);
859226633Sdim		      }
860226633Sdimnextline:
861249423Sdim			if (mp)
862206083Srdivacky				free(mp);
863198090Srdivacky		}
864206083Srdivacky		(void) fclose(fp);
865206083Srdivacky	}
866206083Srdivacky}
867206083Srdivacky
868206083Srdivackystatic void
869198090Srdivackyselecthost(union sockunion *su)
870198090Srdivacky{
871198090Srdivacky	struct ftphost	*hrp;
872198090Srdivacky	u_int16_t port;
873198090Srdivacky#ifdef INET6
874198090Srdivacky	struct in6_addr *mapped_in6 = NULL;
875198090Srdivacky#endif
876198090Srdivacky	struct addrinfo *hi;
877198090Srdivacky
878198090Srdivacky#ifdef INET6
879198090Srdivacky	/*
880198090Srdivacky	 * XXX IPv4 mapped IPv6 addr consideraton,
881198090Srdivacky	 * specified in rfc2373.
882198090Srdivacky	 */
883198090Srdivacky	if (su->su_family == AF_INET6 &&
884198090Srdivacky	    IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
885198090Srdivacky		mapped_in6 = &su->su_sin6.sin6_addr;
886198090Srdivacky#endif
887226633Sdim
888198090Srdivacky	hrp = thishost = firsthost;	/* default */
889198090Srdivacky	port = su->su_port;
890198090Srdivacky	su->su_port = 0;
891198090Srdivacky	while (hrp != NULL) {
892210299Sed	    for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) {
893210299Sed		if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) {
894198090Srdivacky			thishost = hrp;
895198090Srdivacky			break;
896198090Srdivacky		}
897198090Srdivacky#ifdef INET6
898198090Srdivacky		/* XXX IPv4 mapped IPv6 addr consideraton */
899198090Srdivacky		if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL &&
900198090Srdivacky		    (memcmp(&mapped_in6->s6_addr[12],
901205218Srdivacky			    &((struct sockaddr_in *)hi->ai_addr)->sin_addr,
902205218Srdivacky			    sizeof(struct in_addr)) == 0)) {
903205218Srdivacky			thishost = hrp;
904205218Srdivacky			break;
905205218Srdivacky		}
906205218Srdivacky#endif
907226633Sdim	    }
908243830Sdim	    hrp = hrp->next;
909243830Sdim	}
910243830Sdim	su->su_port = port;
911243830Sdim	/* setup static variables as appropriate */
912243830Sdim	hostname = thishost->hostname;
913243830Sdim	ftpuser = thishost->anonuser;
914243830Sdim}
915243830Sdim#endif
916243830Sdim
917243830Sdim/*
918243830Sdim * Helper function for sgetpwnam().
919198090Srdivacky */
920198090Srdivackystatic char *
921218893Sdimsgetsave(char *s)
922218893Sdim{
923226633Sdim	char *new = malloc((unsigned) strlen(s) + 1);
924198090Srdivacky
925249423Sdim	if (new == NULL) {
926249423Sdim		perror_reply(421, "Local resource failure: malloc");
927198090Srdivacky		dologout(1);
928198090Srdivacky		/* NOTREACHED */
929207618Srdivacky	}
930207618Srdivacky	(void) strcpy(new, s);
931249423Sdim	return (new);
932226633Sdim}
933243830Sdim
934243830Sdim/*
935218893Sdim * Save the result of a getpwnam.  Used for USER command, since
936218893Sdim * the data returned must not be clobbered by any other command
937210299Sed * (e.g., globbing).
938249423Sdim */
939210299Sedstatic struct passwd *
940243830Sdimsgetpwnam(char *name)
941243830Sdim{
942243830Sdim	static struct passwd save;
943198090Srdivacky	struct passwd *p;
944207618Srdivacky
945198090Srdivacky	if ((p = getpwnam(name)) == NULL)
946198090Srdivacky		return (p);
947243830Sdim	if (save.pw_name) {
948226633Sdim		free(save.pw_name);
949249423Sdim		free(save.pw_passwd);
950249423Sdim		free(save.pw_gecos);
951198090Srdivacky		free(save.pw_dir);
952226633Sdim		free(save.pw_shell);
953207618Srdivacky	}
954198090Srdivacky	save = *p;
955207618Srdivacky	save.pw_name = sgetsave(p->pw_name);
956243830Sdim	save.pw_passwd = sgetsave(p->pw_passwd);
957198090Srdivacky	save.pw_gecos = sgetsave(p->pw_gecos);
958210299Sed	save.pw_dir = sgetsave(p->pw_dir);
959210299Sed	save.pw_shell = sgetsave(p->pw_shell);
960210299Sed	return (&save);
961249423Sdim}
962249423Sdim
963198090Srdivackystatic int login_attempts;	/* number of failed login attempts */
964198090Srdivackystatic int askpasswd;		/* had user command, ask for passwd */
965207618Srdivackystatic char curname[MAXLOGNAME];	/* current USER name */
966224145Sdim
967243830Sdim/*
968198090Srdivacky * USER command.
969249423Sdim * Sets global passwd pointer pw if named account exists and is acceptable;
970249423Sdim * sets askpasswd if a PASS command is expected.  If logged in previously,
971198090Srdivacky * need to reset state.  If name is "ftp" or "anonymous", the name is not in
972198090Srdivacky * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
973207618Srdivacky * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
974207618Srdivacky * requesting login privileges.  Disallow anyone who does not have a standard
975207618Srdivacky * shell as returned by getusershell().  Disallow anyone mentioned in the file
976198090Srdivacky * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
977198090Srdivacky */
978243830Sdimvoid
979249423Sdimuser(char *name)
980208599Srdivacky{
981243830Sdim	char *cp, *shell;
982243830Sdim
983243830Sdim	if (logged_in) {
984243830Sdim		if (guest) {
985243830Sdim			reply(530, "Can't change user from guest login.");
986243830Sdim			return;
987243830Sdim		} else if (dochroot) {
988243830Sdim			reply(530, "Can't change user from chroot user.");
989249423Sdim			return;
990243830Sdim		}
991243830Sdim		end_login();
992198090Srdivacky	}
993198090Srdivacky
994198090Srdivacky	guest = 0;
995226633Sdim	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
996207618Srdivacky		if (checkuser(_PATH_FTPUSERS, "ftp", 0) ||
997207618Srdivacky		    checkuser(_PATH_FTPUSERS, "anonymous", 0))
998207618Srdivacky			reply(530, "User %s access denied.", name);
999207618Srdivacky#ifdef VIRTUAL_HOSTING
1000249423Sdim		else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
1001226633Sdim#else
1002249423Sdim		else if ((pw = sgetpwnam("ftp")) != NULL) {
1003198090Srdivacky#endif
1004198090Srdivacky			guest = 1;
1005198090Srdivacky			askpasswd = 1;
1006198090Srdivacky			reply(331,
1007198090Srdivacky			"Guest login ok, send your email address as password.");
1008198090Srdivacky		} else
1009198090Srdivacky			reply(530, "User %s unknown.", name);
1010198090Srdivacky		if (!askpasswd && logging)
1011198090Srdivacky			syslog(LOG_NOTICE,
1012198090Srdivacky			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
1013198090Srdivacky		return;
1014198090Srdivacky	}
1015198090Srdivacky	if (anon_only != 0) {
1016198090Srdivacky		reply(530, "Sorry, only anonymous ftp allowed.");
1017198090Srdivacky		return;
1018198090Srdivacky	}
1019198090Srdivacky
1020	if ((pw = sgetpwnam(name))) {
1021		if ((shell = pw->pw_shell) == NULL || *shell == 0)
1022			shell = _PATH_BSHELL;
1023		while ((cp = getusershell()) != NULL)
1024			if (strcmp(cp, shell) == 0)
1025				break;
1026		endusershell();
1027
1028		if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) {
1029			reply(530, "User %s access denied.", name);
1030			if (logging)
1031				syslog(LOG_NOTICE,
1032				    "FTP LOGIN REFUSED FROM %s, %s",
1033				    remotehost, name);
1034			pw = (struct passwd *) NULL;
1035			return;
1036		}
1037	}
1038	if (logging)
1039		strncpy(curname, name, sizeof(curname)-1);
1040
1041	pwok = 0;
1042#ifdef USE_PAM
1043	/* XXX Kluge! The conversation mechanism needs to be fixed. */
1044#endif
1045	if (opiechallenge(&opiedata, name, opieprompt) == 0) {
1046		pwok = (pw != NULL) &&
1047		       opieaccessfile(remotehost) &&
1048		       opiealways(pw->pw_dir);
1049		reply(331, "Response to %s %s for %s.",
1050		      opieprompt, pwok ? "requested" : "required", name);
1051	} else {
1052		pwok = 1;
1053		reply(331, "Password required for %s.", name);
1054	}
1055	askpasswd = 1;
1056	/*
1057	 * Delay before reading passwd after first failed
1058	 * attempt to slow down passwd-guessing programs.
1059	 */
1060	if (login_attempts)
1061		sleep((unsigned) login_attempts);
1062}
1063
1064/*
1065 * Check if a user is in the file "fname"
1066 */
1067static int
1068checkuser(char *fname, char *name, int pwset)
1069{
1070	FILE *fd;
1071	int found = 0;
1072	size_t len;
1073	char *line, *mp, *p;
1074
1075	if ((fd = fopen(fname, "r")) != NULL) {
1076		while (!found && (line = fgetln(fd, &len)) != NULL) {
1077			/* skip comments */
1078			if (line[0] == '#')
1079				continue;
1080			if (line[len - 1] == '\n') {
1081				line[len - 1] = '\0';
1082				mp = NULL;
1083			} else {
1084				if ((mp = malloc(len + 1)) == NULL)
1085					fatalerror("Ran out of memory.");
1086				memcpy(mp, line, len);
1087				mp[len] = '\0';
1088				line = mp;
1089			}
1090			/* avoid possible leading and trailing whitespace */
1091			p = strtok(line, " \t");
1092			/* skip empty lines */
1093			if (p == NULL)
1094				goto nextline;
1095			/*
1096			 * if first chr is '@', check group membership
1097			 */
1098			if (p[0] == '@') {
1099				int i = 0;
1100				struct group *grp;
1101
1102				if ((grp = getgrnam(p+1)) == NULL)
1103					goto nextline;
1104				/*
1105				 * Check user's default group
1106				 */
1107				if (pwset && grp->gr_gid == pw->pw_gid)
1108					found = 1;
1109				/*
1110				 * Check supplementary groups
1111				 */
1112				while (!found && grp->gr_mem[i])
1113					found = strcmp(name,
1114						grp->gr_mem[i++])
1115						== 0;
1116			}
1117			/*
1118			 * Otherwise, just check for username match
1119			 */
1120			else
1121				found = strcmp(p, name) == 0;
1122nextline:
1123			if (mp)
1124				free(mp);
1125		}
1126		(void) fclose(fd);
1127	}
1128	return (found);
1129}
1130
1131/*
1132 * Terminate login as previous user, if any, resetting state;
1133 * used when USER command is given or login fails.
1134 */
1135static void
1136end_login(void)
1137{
1138#ifdef USE_PAM
1139	int e;
1140#endif
1141
1142	(void) seteuid((uid_t)0);
1143	if (logged_in && dowtmp)
1144		ftpd_logwtmp(ttyline, "", NULL);
1145	pw = NULL;
1146#ifdef	LOGIN_CAP
1147	setusercontext(NULL, getpwuid(0), (uid_t)0,
1148		       LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1149#endif
1150#ifdef USE_PAM
1151	if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
1152		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
1153	if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
1154		syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e));
1155	if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
1156		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1157	pamh = NULL;
1158#endif
1159	logged_in = 0;
1160	guest = 0;
1161	dochroot = 0;
1162}
1163
1164#ifdef USE_PAM
1165
1166/*
1167 * the following code is stolen from imap-uw PAM authentication module and
1168 * login.c
1169 */
1170#define COPY_STRING(s) (s ? strdup(s) : NULL)
1171
1172struct cred_t {
1173	const char *uname;		/* user name */
1174	const char *pass;		/* password */
1175};
1176typedef struct cred_t cred_t;
1177
1178static int
1179auth_conv(int num_msg, const struct pam_message **msg,
1180	  struct pam_response **resp, void *appdata)
1181{
1182	int i;
1183	cred_t *cred = (cred_t *) appdata;
1184	struct pam_response *reply;
1185
1186	reply = calloc(num_msg, sizeof *reply);
1187	if (reply == NULL)
1188		return PAM_BUF_ERR;
1189
1190	for (i = 0; i < num_msg; i++) {
1191		switch (msg[i]->msg_style) {
1192		case PAM_PROMPT_ECHO_ON:	/* assume want user name */
1193			reply[i].resp_retcode = PAM_SUCCESS;
1194			reply[i].resp = COPY_STRING(cred->uname);
1195			/* PAM frees resp. */
1196			break;
1197		case PAM_PROMPT_ECHO_OFF:	/* assume want password */
1198			reply[i].resp_retcode = PAM_SUCCESS;
1199			reply[i].resp = COPY_STRING(cred->pass);
1200			/* PAM frees resp. */
1201			break;
1202		case PAM_TEXT_INFO:
1203		case PAM_ERROR_MSG:
1204			reply[i].resp_retcode = PAM_SUCCESS;
1205			reply[i].resp = NULL;
1206			break;
1207		default:			/* unknown message style */
1208			free(reply);
1209			return PAM_CONV_ERR;
1210		}
1211	}
1212
1213	*resp = reply;
1214	return PAM_SUCCESS;
1215}
1216
1217/*
1218 * Attempt to authenticate the user using PAM.  Returns 0 if the user is
1219 * authenticated, or 1 if not authenticated.  If some sort of PAM system
1220 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
1221 * function returns -1.  This can be used as an indication that we should
1222 * fall back to a different authentication mechanism.
1223 */
1224static int
1225auth_pam(struct passwd **ppw, const char *pass)
1226{
1227	pam_handle_t *pamh = NULL;
1228	const char *tmpl_user;
1229	const void *item;
1230	int rval;
1231	int e;
1232	cred_t auth_cred = { (*ppw)->pw_name, pass };
1233	struct pam_conv conv = { &auth_conv, &auth_cred };
1234
1235	e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh);
1236	if (e != PAM_SUCCESS) {
1237		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
1238		return -1;
1239	}
1240
1241	e = pam_set_item(pamh, PAM_RHOST, remotehost);
1242	if (e != PAM_SUCCESS) {
1243		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
1244			pam_strerror(pamh, e));
1245		return -1;
1246	}
1247
1248	e = pam_authenticate(pamh, 0);
1249	switch (e) {
1250	case PAM_SUCCESS:
1251		/*
1252		 * With PAM we support the concept of a "template"
1253		 * user.  The user enters a login name which is
1254		 * authenticated by PAM, usually via a remote service
1255		 * such as RADIUS or TACACS+.  If authentication
1256		 * succeeds, a different but related "template" name
1257		 * is used for setting the credentials, shell, and
1258		 * home directory.  The name the user enters need only
1259		 * exist on the remote authentication server, but the
1260		 * template name must be present in the local password
1261		 * database.
1262		 *
1263		 * This is supported by two various mechanisms in the
1264		 * individual modules.  However, from the application's
1265		 * point of view, the template user is always passed
1266		 * back as a changed value of the PAM_USER item.
1267		 */
1268		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
1269		    PAM_SUCCESS) {
1270			tmpl_user = (const char *) item;
1271			if (strcmp((*ppw)->pw_name, tmpl_user) != 0)
1272				*ppw = getpwnam(tmpl_user);
1273		} else
1274			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
1275			    pam_strerror(pamh, e));
1276		rval = 0;
1277		break;
1278
1279	case PAM_AUTH_ERR:
1280	case PAM_USER_UNKNOWN:
1281	case PAM_MAXTRIES:
1282		rval = 1;
1283		break;
1284
1285	default:
1286		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
1287		rval = -1;
1288		break;
1289	}
1290
1291	if (rval == 0) {
1292		e = pam_acct_mgmt(pamh, 0);
1293		if (e == PAM_NEW_AUTHTOK_REQD) {
1294			e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
1295			if (e != PAM_SUCCESS) {
1296				syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e));
1297				rval = 1;
1298			}
1299		} else if (e != PAM_SUCCESS) {
1300			rval = 1;
1301		}
1302	}
1303
1304	if (rval != 0) {
1305		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
1306			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1307		}
1308		pamh = NULL;
1309	}
1310	return rval;
1311}
1312
1313#endif /* USE_PAM */
1314
1315void
1316pass(char *passwd)
1317{
1318	int rval;
1319	FILE *fd;
1320#ifdef	LOGIN_CAP
1321	login_cap_t *lc = NULL;
1322#endif
1323#ifdef USE_PAM
1324	int e;
1325#endif
1326	char *xpasswd;
1327
1328	if (logged_in || askpasswd == 0) {
1329		reply(503, "Login with USER first.");
1330		return;
1331	}
1332	askpasswd = 0;
1333	if (!guest) {		/* "ftp" is only account allowed no password */
1334		if (pw == NULL) {
1335			rval = 1;	/* failure below */
1336			goto skip;
1337		}
1338#ifdef USE_PAM
1339		rval = auth_pam(&pw, passwd);
1340		if (rval >= 0) {
1341			opieunlock();
1342			goto skip;
1343		}
1344#endif
1345		if (opieverify(&opiedata, passwd) == 0)
1346			xpasswd = pw->pw_passwd;
1347		else if (pwok) {
1348			xpasswd = crypt(passwd, pw->pw_passwd);
1349			if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0')
1350				xpasswd = ":";
1351		} else {
1352			rval = 1;
1353			goto skip;
1354		}
1355		rval = strcmp(pw->pw_passwd, xpasswd);
1356		if (pw->pw_expire && time(NULL) >= pw->pw_expire)
1357			rval = 1;	/* failure */
1358skip:
1359		/*
1360		 * If rval == 1, the user failed the authentication check
1361		 * above.  If rval == 0, either PAM or local authentication
1362		 * succeeded.
1363		 */
1364		if (rval) {
1365			reply(530, "Login incorrect.");
1366			if (logging)
1367				syslog(LOG_NOTICE,
1368				    "FTP LOGIN FAILED FROM %s, %s",
1369				    remotehost, curname);
1370			pw = NULL;
1371			if (login_attempts++ >= 5) {
1372				syslog(LOG_NOTICE,
1373				    "repeated login failures from %s",
1374				    remotehost);
1375				exit(0);
1376			}
1377			return;
1378		}
1379	}
1380	login_attempts = 0;		/* this time successful */
1381	if (setegid((gid_t)pw->pw_gid) < 0) {
1382		reply(550, "Can't set gid.");
1383		return;
1384	}
1385	/* May be overridden by login.conf */
1386	(void) umask(defumask);
1387#ifdef	LOGIN_CAP
1388	if ((lc = login_getpwclass(pw)) != NULL) {
1389		char	remote_ip[MAXHOSTNAMELEN];
1390
1391		getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1392			remote_ip, sizeof(remote_ip) - 1, NULL, 0,
1393			NI_NUMERICHOST);
1394		remote_ip[sizeof(remote_ip) - 1] = 0;
1395		if (!auth_hostok(lc, remotehost, remote_ip)) {
1396			syslog(LOG_INFO|LOG_AUTH,
1397			    "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1398			    pw->pw_name);
1399			reply(530, "Permission denied.\n");
1400			pw = NULL;
1401			return;
1402		}
1403		if (!auth_timeok(lc, time(NULL))) {
1404			reply(530, "Login not available right now.\n");
1405			pw = NULL;
1406			return;
1407		}
1408	}
1409	setusercontext(lc, pw, (uid_t)0,
1410		LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY|
1411		LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1412#else
1413	setlogin(pw->pw_name);
1414	(void) initgroups(pw->pw_name, pw->pw_gid);
1415#endif
1416
1417#ifdef USE_PAM
1418	if (pamh) {
1419		if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
1420			syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
1421		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1422			syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
1423		}
1424	}
1425#endif
1426
1427	/* open wtmp before chroot */
1428	if (dowtmp)
1429		ftpd_logwtmp(ttyline, pw->pw_name,
1430		    (struct sockaddr *)&his_addr);
1431	logged_in = 1;
1432
1433	if (guest && stats && statfd < 0)
1434#ifdef VIRTUAL_HOSTING
1435		if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0)
1436#else
1437		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
1438#endif
1439			stats = 0;
1440
1441	dochroot =
1442#ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
1443		login_getcapbool(lc, "ftp-chroot", 0) ||
1444#endif
1445		checkuser(_PATH_FTPCHROOT, pw->pw_name, 1);
1446	if (guest) {
1447		/*
1448		 * We MUST do a chdir() after the chroot. Otherwise
1449		 * the old current directory will be accessible as "."
1450		 * outside the new root!
1451		 */
1452		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
1453			reply(550, "Can't set guest privileges.");
1454			goto bad;
1455		}
1456	} else if (dochroot) {
1457		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
1458			reply(550, "Can't change root.");
1459			goto bad;
1460		}
1461	} else if (chdir(pw->pw_dir) < 0) {
1462		if (chdir("/") < 0) {
1463			reply(530, "User %s: can't change directory to %s.",
1464			    pw->pw_name, pw->pw_dir);
1465			goto bad;
1466		} else
1467			lreply(230, "No directory! Logging in with home=/");
1468	}
1469	if (seteuid((uid_t)pw->pw_uid) < 0) {
1470		reply(550, "Can't set uid.");
1471		goto bad;
1472	}
1473
1474	/*
1475	 * Display a login message, if it exists.
1476	 * N.B. reply(230,) must follow the message.
1477	 */
1478#ifdef VIRTUAL_HOSTING
1479	if ((fd = fopen(thishost->loginmsg, "r")) != NULL) {
1480#else
1481	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
1482#endif
1483		char *cp, line[LINE_MAX];
1484
1485		while (fgets(line, sizeof(line), fd) != NULL) {
1486			if ((cp = strchr(line, '\n')) != NULL)
1487				*cp = '\0';
1488			lreply(230, "%s", line);
1489		}
1490		(void) fflush(stdout);
1491		(void) fclose(fd);
1492	}
1493	if (guest) {
1494		if (ident != NULL)
1495			free(ident);
1496		ident = strdup(passwd);
1497		if (ident == NULL)
1498			fatalerror("Ran out of memory.");
1499
1500		reply(230, "Guest login ok, access restrictions apply.");
1501#ifdef SETPROCTITLE
1502#ifdef VIRTUAL_HOSTING
1503		if (thishost != firsthost)
1504			snprintf(proctitle, sizeof(proctitle),
1505				 "%s: anonymous(%s)/%s", remotehost, hostname,
1506				 passwd);
1507		else
1508#endif
1509			snprintf(proctitle, sizeof(proctitle),
1510				 "%s: anonymous/%s", remotehost, passwd);
1511		setproctitle("%s", proctitle);
1512#endif /* SETPROCTITLE */
1513		if (logging)
1514			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
1515			    remotehost, passwd);
1516	} else {
1517		if (dochroot)
1518			reply(230, "User %s logged in, "
1519				   "access restrictions apply.", pw->pw_name);
1520		else
1521			reply(230, "User %s logged in.", pw->pw_name);
1522
1523#ifdef SETPROCTITLE
1524		snprintf(proctitle, sizeof(proctitle),
1525			 "%s: user/%s", remotehost, pw->pw_name);
1526		setproctitle("%s", proctitle);
1527#endif /* SETPROCTITLE */
1528		if (logging)
1529			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
1530			    remotehost, pw->pw_name);
1531	}
1532#ifdef	LOGIN_CAP
1533	login_close(lc);
1534#endif
1535	return;
1536bad:
1537	/* Forget all about it... */
1538#ifdef	LOGIN_CAP
1539	login_close(lc);
1540#endif
1541	end_login();
1542}
1543
1544void
1545retrieve(char *cmd, char *name)
1546{
1547	FILE *fin, *dout;
1548	struct stat st;
1549	int (*closefunc)(FILE *);
1550	time_t start;
1551
1552	if (cmd == 0) {
1553		fin = fopen(name, "r"), closefunc = fclose;
1554		st.st_size = 0;
1555	} else {
1556		char line[BUFSIZ];
1557
1558		(void) snprintf(line, sizeof(line), cmd, name), name = line;
1559		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
1560		st.st_size = -1;
1561		st.st_blksize = BUFSIZ;
1562	}
1563	if (fin == NULL) {
1564		if (errno != 0) {
1565			perror_reply(550, name);
1566			if (cmd == 0) {
1567				LOGCMD("get", name);
1568			}
1569		}
1570		return;
1571	}
1572	byte_count = -1;
1573	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
1574		reply(550, "%s: not a plain file.", name);
1575		goto done;
1576	}
1577	if (restart_point) {
1578		if (type == TYPE_A) {
1579			off_t i, n;
1580			int c;
1581
1582			n = restart_point;
1583			i = 0;
1584			while (i++ < n) {
1585				if ((c=getc(fin)) == EOF) {
1586					perror_reply(550, name);
1587					goto done;
1588				}
1589				if (c == '\n')
1590					i++;
1591			}
1592		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
1593			perror_reply(550, name);
1594			goto done;
1595		}
1596	}
1597	dout = dataconn(name, st.st_size, "w");
1598	if (dout == NULL)
1599		goto done;
1600	time(&start);
1601	send_data(fin, dout, st.st_blksize, st.st_size,
1602		  restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
1603	if (cmd == 0 && guest && stats)
1604		logxfer(name, st.st_size, start);
1605	(void) fclose(dout);
1606	data = -1;
1607	pdata = -1;
1608done:
1609	if (cmd == 0)
1610		LOGBYTES("get", name, byte_count);
1611	(*closefunc)(fin);
1612}
1613
1614void
1615store(char *name, char *mode, int unique)
1616{
1617	int fd;
1618	FILE *fout, *din;
1619	int (*closefunc)(FILE *);
1620
1621	if (*mode == 'a') {		/* APPE */
1622		if (unique) {
1623			/* Programming error */
1624			syslog(LOG_ERR, "Internal: unique flag to APPE");
1625			unique = 0;
1626		}
1627		if (guest && noguestmod) {
1628			reply(550, "Appending to existing file denied");
1629			goto err;
1630		}
1631		restart_point = 0;	/* not affected by preceding REST */
1632	}
1633	if (unique)			/* STOU overrides REST */
1634		restart_point = 0;
1635	if (guest && noguestmod) {
1636		if (restart_point) {	/* guest STOR w/REST */
1637			reply(550, "Modifying existing file denied");
1638			goto err;
1639		} else			/* treat guest STOR as STOU */
1640			unique = 1;
1641	}
1642
1643	if (restart_point)
1644		mode = "r+";	/* so ASCII manual seek can work */
1645	if (unique) {
1646		if ((fd = guniquefd(name, &name)) < 0)
1647			goto err;
1648		fout = fdopen(fd, mode);
1649	} else
1650		fout = fopen(name, mode);
1651	closefunc = fclose;
1652	if (fout == NULL) {
1653		perror_reply(553, name);
1654		goto err;
1655	}
1656	byte_count = -1;
1657	if (restart_point) {
1658		if (type == TYPE_A) {
1659			off_t i, n;
1660			int c;
1661
1662			n = restart_point;
1663			i = 0;
1664			while (i++ < n) {
1665				if ((c=getc(fout)) == EOF) {
1666					perror_reply(550, name);
1667					goto done;
1668				}
1669				if (c == '\n')
1670					i++;
1671			}
1672			/*
1673			 * We must do this seek to "current" position
1674			 * because we are changing from reading to
1675			 * writing.
1676			 */
1677			if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) {
1678				perror_reply(550, name);
1679				goto done;
1680			}
1681		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
1682			perror_reply(550, name);
1683			goto done;
1684		}
1685	}
1686	din = dataconn(name, (off_t)-1, "r");
1687	if (din == NULL)
1688		goto done;
1689	if (receive_data(din, fout) == 0) {
1690		if (unique)
1691			reply(226, "Transfer complete (unique file name:%s).",
1692			    name);
1693		else
1694			reply(226, "Transfer complete.");
1695	}
1696	(void) fclose(din);
1697	data = -1;
1698	pdata = -1;
1699done:
1700	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
1701	(*closefunc)(fout);
1702	return;
1703err:
1704	LOGCMD(*mode == 'a' ? "append" : "put" , name);
1705	return;
1706}
1707
1708static FILE *
1709getdatasock(char *mode)
1710{
1711	int on = 1, s, t, tries;
1712
1713	if (data >= 0)
1714		return (fdopen(data, mode));
1715	(void) seteuid((uid_t)0);
1716
1717	s = socket(data_dest.su_family, SOCK_STREAM, 0);
1718	if (s < 0)
1719		goto bad;
1720	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
1721		syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m");
1722	/* anchor socket to avoid multi-homing problems */
1723	data_source = ctrl_addr;
1724	data_source.su_port = htons(20); /* ftp-data port */
1725	for (tries = 1; ; tries++) {
1726		if (bind(s, (struct sockaddr *)&data_source,
1727		    data_source.su_len) >= 0)
1728			break;
1729		if (errno != EADDRINUSE || tries > 10)
1730			goto bad;
1731		sleep(tries);
1732	}
1733	(void) seteuid((uid_t)pw->pw_uid);
1734#ifdef IP_TOS
1735	if (data_source.su_family == AF_INET)
1736      {
1737	on = IPTOS_THROUGHPUT;
1738	if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0)
1739		syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m");
1740      }
1741#endif
1742#ifdef TCP_NOPUSH
1743	/*
1744	 * Turn off push flag to keep sender TCP from sending short packets
1745	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
1746	 * to set the send buffer size as well, but that may not be desirable
1747	 * in heavy-load situations.
1748	 */
1749	on = 1;
1750	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0)
1751		syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m");
1752#endif
1753#ifdef SO_SNDBUF
1754	on = 65536;
1755	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof on) < 0)
1756		syslog(LOG_WARNING, "data setsockopt (SO_SNDBUF): %m");
1757#endif
1758
1759	return (fdopen(s, mode));
1760bad:
1761	/* Return the real value of errno (close may change it) */
1762	t = errno;
1763	(void) seteuid((uid_t)pw->pw_uid);
1764	(void) close(s);
1765	errno = t;
1766	return (NULL);
1767}
1768
1769static FILE *
1770dataconn(char *name, off_t size, char *mode)
1771{
1772	char sizebuf[32];
1773	FILE *file;
1774	int retry = 0, tos;
1775
1776	file_size = size;
1777	byte_count = 0;
1778	if (size != (off_t) -1)
1779		(void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size);
1780	else
1781		*sizebuf = '\0';
1782	if (pdata >= 0) {
1783		union sockunion from;
1784		int flags;
1785		int s, fromlen = ctrl_addr.su_len;
1786		struct timeval timeout;
1787		fd_set set;
1788
1789		FD_ZERO(&set);
1790		FD_SET(pdata, &set);
1791
1792		timeout.tv_usec = 0;
1793		timeout.tv_sec = 120;
1794
1795		/*
1796		 * Granted a socket is in the blocking I/O mode,
1797		 * accept() will block after a successful select()
1798		 * if the selected connection dies in between.
1799		 * Therefore set the non-blocking I/O flag here.
1800		 */
1801		if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
1802		    fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1)
1803			goto pdata_err;
1804		if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) <= 0 ||
1805		    (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0)
1806			goto pdata_err;
1807		(void) close(pdata);
1808		pdata = s;
1809		/*
1810		 * Unset the inherited non-blocking I/O flag
1811		 * on the child socket so stdio can work on it.
1812		 */
1813		if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
1814		    fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1)
1815			goto pdata_err;
1816#ifdef IP_TOS
1817		if (from.su_family == AF_INET)
1818	      {
1819		tos = IPTOS_THROUGHPUT;
1820		if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
1821			syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m");
1822	      }
1823#endif
1824		reply(150, "Opening %s mode data connection for '%s'%s.",
1825		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1826		return (fdopen(pdata, mode));
1827pdata_err:
1828		reply(425, "Can't open data connection.");
1829		(void) close(pdata);
1830		pdata = -1;
1831		return (NULL);
1832	}
1833	if (data >= 0) {
1834		reply(125, "Using existing data connection for '%s'%s.",
1835		    name, sizebuf);
1836		usedefault = 1;
1837		return (fdopen(data, mode));
1838	}
1839	if (usedefault)
1840		data_dest = his_addr;
1841	usedefault = 1;
1842	file = getdatasock(mode);
1843	if (file == NULL) {
1844		char hostbuf[BUFSIZ], portbuf[BUFSIZ];
1845		getnameinfo((struct sockaddr *)&data_source,
1846			data_source.su_len, hostbuf, sizeof(hostbuf) - 1,
1847			portbuf, sizeof(portbuf),
1848			NI_NUMERICHOST|NI_NUMERICSERV);
1849		reply(425, "Can't create data socket (%s,%s): %s.",
1850			hostbuf, portbuf, strerror(errno));
1851		return (NULL);
1852	}
1853	data = fileno(file);
1854	while (connect(data, (struct sockaddr *)&data_dest,
1855	    data_dest.su_len) < 0) {
1856		if (errno == EADDRINUSE && retry < swaitmax) {
1857			sleep((unsigned) swaitint);
1858			retry += swaitint;
1859			continue;
1860		}
1861		perror_reply(425, "Can't build data connection");
1862		(void) fclose(file);
1863		data = -1;
1864		return (NULL);
1865	}
1866	reply(150, "Opening %s mode data connection for '%s'%s.",
1867	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1868	return (file);
1869}
1870
1871/*
1872 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1873 * encapsulation of the data subject to Mode, Structure, and Type.
1874 *
1875 * NB: Form isn't handled.
1876 */
1877static int
1878send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
1879{
1880	int c, filefd, netfd;
1881	char *buf;
1882	off_t cnt;
1883
1884	transflag++;
1885	switch (type) {
1886
1887	case TYPE_A:
1888		while ((c = getc(instr)) != EOF) {
1889			if (recvurg)
1890				goto got_oob;
1891			byte_count++;
1892			if (c == '\n') {
1893				if (ferror(outstr))
1894					goto data_err;
1895				(void) putc('\r', outstr);
1896			}
1897			(void) putc(c, outstr);
1898		}
1899		if (recvurg)
1900			goto got_oob;
1901		fflush(outstr);
1902		transflag = 0;
1903		if (ferror(instr))
1904			goto file_err;
1905		if (ferror(outstr))
1906			goto data_err;
1907		reply(226, "Transfer complete.");
1908		return (0);
1909
1910	case TYPE_I:
1911	case TYPE_L:
1912		/*
1913		 * isreg is only set if we are not doing restart and we
1914		 * are sending a regular file
1915		 */
1916		netfd = fileno(outstr);
1917		filefd = fileno(instr);
1918
1919		if (isreg) {
1920
1921			off_t offset;
1922			int err;
1923
1924			err = cnt = offset = 0;
1925
1926			while (err != -1 && filesize > 0) {
1927				err = sendfile(filefd, netfd, offset, 0,
1928					(struct sf_hdtr *) NULL, &cnt, 0);
1929				/*
1930				 * Calculate byte_count before OOB processing.
1931				 * It can be used in myoob() later.
1932				 */
1933				byte_count += cnt;
1934				if (recvurg)
1935					goto got_oob;
1936				offset += cnt;
1937				filesize -= cnt;
1938
1939				if (err == -1) {
1940					if (!cnt)
1941						goto oldway;
1942
1943					goto data_err;
1944				}
1945			}
1946
1947			transflag = 0;
1948			reply(226, "Transfer complete.");
1949			return (0);
1950		}
1951
1952oldway:
1953		if ((buf = malloc((u_int)blksize)) == NULL) {
1954			transflag = 0;
1955			perror_reply(451, "Local resource failure: malloc");
1956			return (-1);
1957		}
1958
1959		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1960		    write(netfd, buf, cnt) == cnt)
1961			byte_count += cnt;
1962		transflag = 0;
1963		(void)free(buf);
1964		if (cnt != 0) {
1965			if (cnt < 0)
1966				goto file_err;
1967			goto data_err;
1968		}
1969		reply(226, "Transfer complete.");
1970		return (0);
1971	default:
1972		transflag = 0;
1973		reply(550, "Unimplemented TYPE %d in send_data", type);
1974		return (-1);
1975	}
1976
1977data_err:
1978	transflag = 0;
1979	perror_reply(426, "Data connection");
1980	return (-1);
1981
1982file_err:
1983	transflag = 0;
1984	perror_reply(551, "Error on input file");
1985	return (-1);
1986
1987got_oob:
1988	myoob();
1989	recvurg = 0;
1990	transflag = 0;
1991	return (-1);
1992}
1993
1994/*
1995 * Transfer data from peer to "outstr" using the appropriate encapulation of
1996 * the data subject to Mode, Structure, and Type.
1997 *
1998 * N.B.: Form isn't handled.
1999 */
2000static int
2001receive_data(FILE *instr, FILE *outstr)
2002{
2003	int c;
2004	int cnt, bare_lfs;
2005	char buf[BUFSIZ];
2006
2007	transflag++;
2008	bare_lfs = 0;
2009
2010	switch (type) {
2011
2012	case TYPE_I:
2013	case TYPE_L:
2014		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
2015			if (recvurg)
2016				goto got_oob;
2017			if (write(fileno(outstr), buf, cnt) != cnt)
2018				goto file_err;
2019			byte_count += cnt;
2020		}
2021		if (recvurg)
2022			goto got_oob;
2023		if (cnt < 0)
2024			goto data_err;
2025		transflag = 0;
2026		return (0);
2027
2028	case TYPE_E:
2029		reply(553, "TYPE E not implemented.");
2030		transflag = 0;
2031		return (-1);
2032
2033	case TYPE_A:
2034		while ((c = getc(instr)) != EOF) {
2035			if (recvurg)
2036				goto got_oob;
2037			byte_count++;
2038			if (c == '\n')
2039				bare_lfs++;
2040			while (c == '\r') {
2041				if (ferror(outstr))
2042					goto data_err;
2043				if ((c = getc(instr)) != '\n') {
2044					(void) putc ('\r', outstr);
2045					if (c == '\0' || c == EOF)
2046						goto contin2;
2047				}
2048			}
2049			(void) putc(c, outstr);
2050	contin2:	;
2051		}
2052		if (recvurg)
2053			goto got_oob;
2054		fflush(outstr);
2055		if (ferror(instr))
2056			goto data_err;
2057		if (ferror(outstr))
2058			goto file_err;
2059		transflag = 0;
2060		if (bare_lfs) {
2061			lreply(226,
2062		"WARNING! %d bare linefeeds received in ASCII mode",
2063			    bare_lfs);
2064		(void)printf("   File may not have transferred correctly.\r\n");
2065		}
2066		return (0);
2067	default:
2068		reply(550, "Unimplemented TYPE %d in receive_data", type);
2069		transflag = 0;
2070		return (-1);
2071	}
2072
2073data_err:
2074	transflag = 0;
2075	perror_reply(426, "Data Connection");
2076	return (-1);
2077
2078file_err:
2079	transflag = 0;
2080	perror_reply(452, "Error writing file");
2081	return (-1);
2082
2083got_oob:
2084	myoob();
2085	recvurg = 0;
2086	transflag = 0;
2087	return (-1);
2088}
2089
2090void
2091statfilecmd(char *filename)
2092{
2093	FILE *fin;
2094	int c;
2095	char line[LINE_MAX];
2096
2097	(void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename);
2098	fin = ftpd_popen(line, "r");
2099	lreply(211, "status of %s:", filename);
2100	while ((c = getc(fin)) != EOF) {
2101		if (c == '\n') {
2102			if (ferror(stdout)){
2103				perror_reply(421, "control connection");
2104				(void) ftpd_pclose(fin);
2105				dologout(1);
2106				/* NOTREACHED */
2107			}
2108			if (ferror(fin)) {
2109				perror_reply(551, filename);
2110				(void) ftpd_pclose(fin);
2111				return;
2112			}
2113			(void) putc('\r', stdout);
2114		}
2115		(void) putc(c, stdout);
2116	}
2117	(void) ftpd_pclose(fin);
2118	reply(211, "End of Status");
2119}
2120
2121void
2122statcmd(void)
2123{
2124	union sockunion *su;
2125	u_char *a, *p;
2126	char hname[NI_MAXHOST];
2127	int ispassive;
2128
2129	lreply(211, "%s FTP server status:", hostname);
2130	printf("     %s\r\n", version);
2131	printf("     Connected to %s", remotehost);
2132	if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
2133			 hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) {
2134		if (strcmp(hname, remotehost) != 0)
2135			printf(" (%s)", hname);
2136	}
2137	printf("\r\n");
2138	if (logged_in) {
2139		if (guest)
2140			printf("     Logged in anonymously\r\n");
2141		else
2142			printf("     Logged in as %s\r\n", pw->pw_name);
2143	} else if (askpasswd)
2144		printf("     Waiting for password\r\n");
2145	else
2146		printf("     Waiting for user name\r\n");
2147	printf("     TYPE: %s", typenames[type]);
2148	if (type == TYPE_A || type == TYPE_E)
2149		printf(", FORM: %s", formnames[form]);
2150	if (type == TYPE_L)
2151#if NBBY == 8
2152		printf(" %d", NBBY);
2153#else
2154		printf(" %d", bytesize);	/* need definition! */
2155#endif
2156	printf("; STRUcture: %s; transfer MODE: %s\r\n",
2157	    strunames[stru], modenames[mode]);
2158	if (data != -1)
2159		printf("     Data connection open\r\n");
2160	else if (pdata != -1) {
2161		ispassive = 1;
2162		su = &pasv_addr;
2163		goto printaddr;
2164	} else if (usedefault == 0) {
2165		ispassive = 0;
2166		su = &data_dest;
2167printaddr:
2168#define UC(b) (((int) b) & 0xff)
2169		if (epsvall) {
2170			printf("     EPSV only mode (EPSV ALL)\r\n");
2171			goto epsvonly;
2172		}
2173
2174		/* PORT/PASV */
2175		if (su->su_family == AF_INET) {
2176			a = (u_char *) &su->su_sin.sin_addr;
2177			p = (u_char *) &su->su_sin.sin_port;
2178			printf("     %s (%d,%d,%d,%d,%d,%d)\r\n",
2179				ispassive ? "PASV" : "PORT",
2180				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2181				UC(p[0]), UC(p[1]));
2182		}
2183
2184		/* LPRT/LPSV */
2185	    {
2186		int alen, af, i;
2187
2188		switch (su->su_family) {
2189		case AF_INET:
2190			a = (u_char *) &su->su_sin.sin_addr;
2191			p = (u_char *) &su->su_sin.sin_port;
2192			alen = sizeof(su->su_sin.sin_addr);
2193			af = 4;
2194			break;
2195		case AF_INET6:
2196			a = (u_char *) &su->su_sin6.sin6_addr;
2197			p = (u_char *) &su->su_sin6.sin6_port;
2198			alen = sizeof(su->su_sin6.sin6_addr);
2199			af = 6;
2200			break;
2201		default:
2202			af = 0;
2203			break;
2204		}
2205		if (af) {
2206			printf("     %s (%d,%d,", ispassive ? "LPSV" : "LPRT",
2207				af, alen);
2208			for (i = 0; i < alen; i++)
2209				printf("%d,", UC(a[i]));
2210			printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
2211		}
2212	    }
2213
2214epsvonly:;
2215		/* EPRT/EPSV */
2216	    {
2217		int af;
2218
2219		switch (su->su_family) {
2220		case AF_INET:
2221			af = 1;
2222			break;
2223		case AF_INET6:
2224			af = 2;
2225			break;
2226		default:
2227			af = 0;
2228			break;
2229		}
2230		if (af) {
2231			union sockunion tmp;
2232
2233			tmp = *su;
2234			if (tmp.su_family == AF_INET6)
2235				tmp.su_sin6.sin6_scope_id = 0;
2236			if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len,
2237					hname, sizeof(hname) - 1, NULL, 0,
2238					NI_NUMERICHOST)) {
2239				printf("     %s |%d|%s|%d|\r\n",
2240					ispassive ? "EPSV" : "EPRT",
2241					af, hname, htons(tmp.su_port));
2242			}
2243		}
2244	    }
2245#undef UC
2246	} else
2247		printf("     No data connection\r\n");
2248	reply(211, "End of status");
2249}
2250
2251void
2252fatalerror(char *s)
2253{
2254
2255	reply(451, "Error in server: %s\n", s);
2256	reply(221, "Closing connection due to server error.");
2257	dologout(0);
2258	/* NOTREACHED */
2259}
2260
2261void
2262reply(int n, const char *fmt, ...)
2263{
2264	va_list ap;
2265
2266	va_start(ap, fmt);
2267	(void)printf("%d ", n);
2268	(void)vprintf(fmt, ap);
2269	(void)printf("\r\n");
2270	(void)fflush(stdout);
2271	if (ftpdebug) {
2272		syslog(LOG_DEBUG, "<--- %d ", n);
2273		vsyslog(LOG_DEBUG, fmt, ap);
2274	}
2275}
2276
2277void
2278lreply(int n, const char *fmt, ...)
2279{
2280	va_list ap;
2281
2282	va_start(ap, fmt);
2283	(void)printf("%d- ", n);
2284	(void)vprintf(fmt, ap);
2285	(void)printf("\r\n");
2286	(void)fflush(stdout);
2287	if (ftpdebug) {
2288		syslog(LOG_DEBUG, "<--- %d- ", n);
2289		vsyslog(LOG_DEBUG, fmt, ap);
2290	}
2291}
2292
2293static void
2294ack(char *s)
2295{
2296
2297	reply(250, "%s command successful.", s);
2298}
2299
2300void
2301nack(char *s)
2302{
2303
2304	reply(502, "%s command not implemented.", s);
2305}
2306
2307/* ARGSUSED */
2308void
2309yyerror(char *s)
2310{
2311	char *cp;
2312
2313	if ((cp = strchr(cbuf,'\n')))
2314		*cp = '\0';
2315	reply(500, "'%s': command not understood.", cbuf);
2316}
2317
2318void
2319delete(char *name)
2320{
2321	struct stat st;
2322
2323	LOGCMD("delete", name);
2324	if (lstat(name, &st) < 0) {
2325		perror_reply(550, name);
2326		return;
2327	}
2328	if ((st.st_mode&S_IFMT) == S_IFDIR) {
2329		if (rmdir(name) < 0) {
2330			perror_reply(550, name);
2331			return;
2332		}
2333		goto done;
2334	}
2335	if (unlink(name) < 0) {
2336		perror_reply(550, name);
2337		return;
2338	}
2339done:
2340	ack("DELE");
2341}
2342
2343void
2344cwd(char *path)
2345{
2346
2347	if (chdir(path) < 0)
2348		perror_reply(550, path);
2349	else
2350		ack("CWD");
2351}
2352
2353void
2354makedir(char *name)
2355{
2356	char *s;
2357
2358	LOGCMD("mkdir", name);
2359	if (guest && noguestmkd)
2360		reply(550, "%s: permission denied", name);
2361	else if (mkdir(name, 0777) < 0)
2362		perror_reply(550, name);
2363	else {
2364		if ((s = doublequote(name)) == NULL)
2365			fatalerror("Ran out of memory.");
2366		reply(257, "\"%s\" directory created.", s);
2367		free(s);
2368	}
2369}
2370
2371void
2372removedir(char *name)
2373{
2374
2375	LOGCMD("rmdir", name);
2376	if (rmdir(name) < 0)
2377		perror_reply(550, name);
2378	else
2379		ack("RMD");
2380}
2381
2382void
2383pwd(void)
2384{
2385	char *s, path[MAXPATHLEN + 1];
2386
2387	if (getwd(path) == (char *)NULL)
2388		reply(550, "%s.", path);
2389	else {
2390		if ((s = doublequote(path)) == NULL)
2391			fatalerror("Ran out of memory.");
2392		reply(257, "\"%s\" is current directory.", s);
2393		free(s);
2394	}
2395}
2396
2397char *
2398renamefrom(char *name)
2399{
2400	struct stat st;
2401
2402	if (lstat(name, &st) < 0) {
2403		perror_reply(550, name);
2404		return ((char *)0);
2405	}
2406	reply(350, "File exists, ready for destination name");
2407	return (name);
2408}
2409
2410void
2411renamecmd(char *from, char *to)
2412{
2413	struct stat st;
2414
2415	LOGCMD2("rename", from, to);
2416
2417	if (guest && (stat(to, &st) == 0)) {
2418		reply(550, "%s: permission denied", to);
2419		return;
2420	}
2421
2422	if (rename(from, to) < 0)
2423		perror_reply(550, "rename");
2424	else
2425		ack("RNTO");
2426}
2427
2428static void
2429dolog(struct sockaddr *who)
2430{
2431	int error;
2432
2433	realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len);
2434
2435#ifdef SETPROCTITLE
2436#ifdef VIRTUAL_HOSTING
2437	if (thishost != firsthost)
2438		snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)",
2439			 remotehost, hostname);
2440	else
2441#endif
2442		snprintf(proctitle, sizeof(proctitle), "%s: connected",
2443			 remotehost);
2444	setproctitle("%s", proctitle);
2445#endif /* SETPROCTITLE */
2446
2447	if (logging) {
2448#ifdef VIRTUAL_HOSTING
2449		if (thishost != firsthost)
2450			syslog(LOG_INFO, "connection from %s (to %s)",
2451			       remotehost, hostname);
2452		else
2453#endif
2454		{
2455			char	who_name[MAXHOSTNAMELEN];
2456
2457			error = getnameinfo(who, who->sa_len,
2458					    who_name, sizeof(who_name) - 1,
2459					    NULL, 0, NI_NUMERICHOST);
2460			syslog(LOG_INFO, "connection from %s (%s)", remotehost,
2461			       error == 0 ? who_name : "");
2462		}
2463	}
2464}
2465
2466/*
2467 * Record logout in wtmp file
2468 * and exit with supplied status.
2469 */
2470void
2471dologout(int status)
2472{
2473	/*
2474	 * Prevent reception of SIGURG from resulting in a resumption
2475	 * back to the main program loop.
2476	 */
2477	transflag = 0;
2478
2479	if (logged_in && dowtmp) {
2480		(void) seteuid((uid_t)0);
2481		ftpd_logwtmp(ttyline, "", NULL);
2482	}
2483	/* beware of flushing buffers after a SIGPIPE */
2484	_exit(status);
2485}
2486
2487static void
2488sigurg(int signo)
2489{
2490
2491	recvurg = 1;
2492}
2493
2494static void
2495myoob(void)
2496{
2497	char *cp;
2498
2499	/* only process if transfer occurring */
2500	if (!transflag)
2501		return;
2502	cp = tmpline;
2503	if (getline(cp, 7, stdin) == NULL) {
2504		reply(221, "You could at least say goodbye.");
2505		dologout(0);
2506	}
2507	upper(cp);
2508	if (strcmp(cp, "ABOR\r\n") == 0) {
2509		tmpline[0] = '\0';
2510		reply(426, "Transfer aborted. Data connection closed.");
2511		reply(226, "Abort successful");
2512	}
2513	if (strcmp(cp, "STAT\r\n") == 0) {
2514		tmpline[0] = '\0';
2515		if (file_size != (off_t) -1)
2516			reply(213, "Status: %qd of %qd bytes transferred",
2517			    byte_count, file_size);
2518		else
2519			reply(213, "Status: %qd bytes transferred", byte_count);
2520	}
2521}
2522
2523/*
2524 * Note: a response of 425 is not mentioned as a possible response to
2525 *	the PASV command in RFC959. However, it has been blessed as
2526 *	a legitimate response by Jon Postel in a telephone conversation
2527 *	with Rick Adams on 25 Jan 89.
2528 */
2529void
2530passive(void)
2531{
2532	int len, on;
2533	char *p, *a;
2534
2535	if (pdata >= 0)		/* close old port if one set */
2536		close(pdata);
2537
2538	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
2539	if (pdata < 0) {
2540		perror_reply(425, "Can't open passive connection");
2541		return;
2542	}
2543	on = 1;
2544	if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
2545		syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m");
2546
2547	(void) seteuid((uid_t)0);
2548
2549#ifdef IP_PORTRANGE
2550	if (ctrl_addr.su_family == AF_INET) {
2551	    on = restricted_data_ports ? IP_PORTRANGE_HIGH
2552				       : IP_PORTRANGE_DEFAULT;
2553
2554	    if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
2555			    &on, sizeof(on)) < 0)
2556		    goto pasv_error;
2557	}
2558#endif
2559#ifdef IPV6_PORTRANGE
2560	if (ctrl_addr.su_family == AF_INET6) {
2561	    on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
2562				       : IPV6_PORTRANGE_DEFAULT;
2563
2564	    if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
2565			    &on, sizeof(on)) < 0)
2566		    goto pasv_error;
2567	}
2568#endif
2569
2570	pasv_addr = ctrl_addr;
2571	pasv_addr.su_port = 0;
2572	if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
2573		goto pasv_error;
2574
2575	(void) seteuid((uid_t)pw->pw_uid);
2576
2577	len = sizeof(pasv_addr);
2578	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
2579		goto pasv_error;
2580	if (listen(pdata, 1) < 0)
2581		goto pasv_error;
2582	if (pasv_addr.su_family == AF_INET)
2583		a = (char *) &pasv_addr.su_sin.sin_addr;
2584	else if (pasv_addr.su_family == AF_INET6 &&
2585		 IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr))
2586		a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
2587	else
2588		goto pasv_error;
2589
2590	p = (char *) &pasv_addr.su_port;
2591
2592#define UC(b) (((int) b) & 0xff)
2593
2594	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
2595		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
2596	return;
2597
2598pasv_error:
2599	(void) seteuid((uid_t)pw->pw_uid);
2600	(void) close(pdata);
2601	pdata = -1;
2602	perror_reply(425, "Can't open passive connection");
2603	return;
2604}
2605
2606/*
2607 * Long Passive defined in RFC 1639.
2608 *     228 Entering Long Passive Mode
2609 *         (af, hal, h1, h2, h3,..., pal, p1, p2...)
2610 */
2611
2612void
2613long_passive(char *cmd, int pf)
2614{
2615	int len, on;
2616	char *p, *a;
2617
2618	if (pdata >= 0)		/* close old port if one set */
2619		close(pdata);
2620
2621	if (pf != PF_UNSPEC) {
2622		if (ctrl_addr.su_family != pf) {
2623			switch (ctrl_addr.su_family) {
2624			case AF_INET:
2625				pf = 1;
2626				break;
2627			case AF_INET6:
2628				pf = 2;
2629				break;
2630			default:
2631				pf = 0;
2632				break;
2633			}
2634			/*
2635			 * XXX
2636			 * only EPRT/EPSV ready clients will understand this
2637			 */
2638			if (strcmp(cmd, "EPSV") == 0 && pf) {
2639				reply(522, "Network protocol mismatch, "
2640					"use (%d)", pf);
2641			} else
2642				reply(501, "Network protocol mismatch"); /*XXX*/
2643
2644			return;
2645		}
2646	}
2647
2648	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
2649	if (pdata < 0) {
2650		perror_reply(425, "Can't open passive connection");
2651		return;
2652	}
2653	on = 1;
2654	if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
2655		syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m");
2656
2657	(void) seteuid((uid_t)0);
2658
2659	pasv_addr = ctrl_addr;
2660	pasv_addr.su_port = 0;
2661	len = pasv_addr.su_len;
2662
2663#ifdef IP_PORTRANGE
2664	if (ctrl_addr.su_family == AF_INET) {
2665	    on = restricted_data_ports ? IP_PORTRANGE_HIGH
2666				       : IP_PORTRANGE_DEFAULT;
2667
2668	    if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
2669			    &on, sizeof(on)) < 0)
2670		    goto pasv_error;
2671	}
2672#endif
2673#ifdef IPV6_PORTRANGE
2674	if (ctrl_addr.su_family == AF_INET6) {
2675	    on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
2676				       : IPV6_PORTRANGE_DEFAULT;
2677
2678	    if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
2679			    &on, sizeof(on)) < 0)
2680		    goto pasv_error;
2681	}
2682#endif
2683
2684	if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0)
2685		goto pasv_error;
2686
2687	(void) seteuid((uid_t)pw->pw_uid);
2688
2689	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
2690		goto pasv_error;
2691	if (listen(pdata, 1) < 0)
2692		goto pasv_error;
2693
2694#define UC(b) (((int) b) & 0xff)
2695
2696	if (strcmp(cmd, "LPSV") == 0) {
2697		p = (char *)&pasv_addr.su_port;
2698		switch (pasv_addr.su_family) {
2699		case AF_INET:
2700			a = (char *) &pasv_addr.su_sin.sin_addr;
2701		v4_reply:
2702			reply(228,
2703"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
2704			      4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2705			      2, UC(p[0]), UC(p[1]));
2706			return;
2707		case AF_INET6:
2708			if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) {
2709				a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
2710				goto v4_reply;
2711			}
2712			a = (char *) &pasv_addr.su_sin6.sin6_addr;
2713			reply(228,
2714"Entering Long Passive Mode "
2715"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
2716			      6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2717			      UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
2718			      UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
2719			      UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
2720			      2, UC(p[0]), UC(p[1]));
2721			return;
2722		}
2723	} else if (strcmp(cmd, "EPSV") == 0) {
2724		switch (pasv_addr.su_family) {
2725		case AF_INET:
2726		case AF_INET6:
2727			reply(229, "Entering Extended Passive Mode (|||%d|)",
2728				ntohs(pasv_addr.su_port));
2729			return;
2730		}
2731	} else {
2732		/* more proper error code? */
2733	}
2734
2735pasv_error:
2736	(void) seteuid((uid_t)pw->pw_uid);
2737	(void) close(pdata);
2738	pdata = -1;
2739	perror_reply(425, "Can't open passive connection");
2740	return;
2741}
2742
2743/*
2744 * Generate unique name for file with basename "local"
2745 * and open the file in order to avoid possible races.
2746 * Try "local" first, then "local.1", "local.2" etc, up to "local.99".
2747 * Return descriptor to the file, set "name" to its name.
2748 *
2749 * Generates failure reply on error.
2750 */
2751static int
2752guniquefd(char *local, char **name)
2753{
2754	static char new[MAXPATHLEN];
2755	struct stat st;
2756	char *cp;
2757	int count;
2758	int fd;
2759
2760	cp = strrchr(local, '/');
2761	if (cp)
2762		*cp = '\0';
2763	if (stat(cp ? local : ".", &st) < 0) {
2764		perror_reply(553, cp ? local : ".");
2765		return (-1);
2766	}
2767	if (cp) {
2768		/*
2769		 * Let not overwrite dirname with counter suffix.
2770		 * -4 is for /nn\0
2771		 * In this extreme case dot won't be put in front of suffix.
2772		 */
2773		if (strlen(local) > sizeof(new) - 4) {
2774			reply(553, "Pathname too long");
2775			return (-1);
2776		}
2777		*cp = '/';
2778	}
2779	/* -4 is for the .nn<null> we put on the end below */
2780	(void) snprintf(new, sizeof(new) - 4, "%s", local);
2781	cp = new + strlen(new);
2782	/*
2783	 * Don't generate dotfile unless requested explicitly.
2784	 * This covers the case when basename gets truncated off
2785	 * by buffer size.
2786	 */
2787	if (cp > new && cp[-1] != '/')
2788		*cp++ = '.';
2789	for (count = 0; count < 100; count++) {
2790		/* At count 0 try unmodified name */
2791		if (count)
2792			(void)sprintf(cp, "%d", count);
2793		if ((fd = open(count ? new : local,
2794		    O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) {
2795			*name = count ? new : local;
2796			return (fd);
2797		}
2798	}
2799	reply(452, "Unique file name cannot be created.");
2800	return (-1);
2801}
2802
2803/*
2804 * Format and send reply containing system error number.
2805 */
2806void
2807perror_reply(int code, char *string)
2808{
2809
2810	reply(code, "%s: %s.", string, strerror(errno));
2811}
2812
2813static char *onefile[] = {
2814	"",
2815	0
2816};
2817
2818void
2819send_file_list(char *whichf)
2820{
2821	struct stat st;
2822	DIR *dirp = NULL;
2823	struct dirent *dir;
2824	FILE *dout = NULL;
2825	char **dirlist, *dirname;
2826	int simple = 0;
2827	int freeglob = 0;
2828	glob_t gl;
2829
2830	if (strpbrk(whichf, "~{[*?") != NULL) {
2831		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
2832
2833		memset(&gl, 0, sizeof(gl));
2834		gl.gl_matchc = MAXGLOBARGS;
2835		flags |= GLOB_LIMIT;
2836		freeglob = 1;
2837		if (glob(whichf, flags, 0, &gl)) {
2838			reply(550, "not found");
2839			goto out;
2840		} else if (gl.gl_pathc == 0) {
2841			errno = ENOENT;
2842			perror_reply(550, whichf);
2843			goto out;
2844		}
2845		dirlist = gl.gl_pathv;
2846	} else {
2847		onefile[0] = whichf;
2848		dirlist = onefile;
2849		simple = 1;
2850	}
2851
2852	while ((dirname = *dirlist++)) {
2853		if (stat(dirname, &st) < 0) {
2854			/*
2855			 * If user typed "ls -l", etc, and the client
2856			 * used NLST, do what the user meant.
2857			 */
2858			if (dirname[0] == '-' && *dirlist == NULL &&
2859			    transflag == 0) {
2860				retrieve(_PATH_LS " %s", dirname);
2861				goto out;
2862			}
2863			perror_reply(550, whichf);
2864			if (dout != NULL) {
2865				(void) fclose(dout);
2866				transflag = 0;
2867				data = -1;
2868				pdata = -1;
2869			}
2870			goto out;
2871		}
2872
2873		if (S_ISREG(st.st_mode)) {
2874			if (dout == NULL) {
2875				dout = dataconn("file list", (off_t)-1, "w");
2876				if (dout == NULL)
2877					goto out;
2878				transflag++;
2879			}
2880			fprintf(dout, "%s%s\n", dirname,
2881				type == TYPE_A ? "\r" : "");
2882			byte_count += strlen(dirname) + 1;
2883			continue;
2884		} else if (!S_ISDIR(st.st_mode))
2885			continue;
2886
2887		if ((dirp = opendir(dirname)) == NULL)
2888			continue;
2889
2890		while ((dir = readdir(dirp)) != NULL) {
2891			char nbuf[MAXPATHLEN];
2892
2893			if (recvurg) {
2894				myoob();
2895				recvurg = 0;
2896				transflag = 0;
2897				goto out;
2898			}
2899
2900			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
2901				continue;
2902			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
2903			    dir->d_namlen == 2)
2904				continue;
2905
2906			snprintf(nbuf, sizeof(nbuf),
2907				"%s/%s", dirname, dir->d_name);
2908
2909			/*
2910			 * We have to do a stat to insure it's
2911			 * not a directory or special file.
2912			 */
2913			if (simple || (stat(nbuf, &st) == 0 &&
2914			    S_ISREG(st.st_mode))) {
2915				if (dout == NULL) {
2916					dout = dataconn("file list", (off_t)-1,
2917						"w");
2918					if (dout == NULL)
2919						goto out;
2920					transflag++;
2921				}
2922				if (nbuf[0] == '.' && nbuf[1] == '/')
2923					fprintf(dout, "%s%s\n", &nbuf[2],
2924						type == TYPE_A ? "\r" : "");
2925				else
2926					fprintf(dout, "%s%s\n", nbuf,
2927						type == TYPE_A ? "\r" : "");
2928				byte_count += strlen(nbuf) + 1;
2929			}
2930		}
2931		(void) closedir(dirp);
2932	}
2933
2934	if (dout == NULL)
2935		reply(550, "No files found.");
2936	else if (ferror(dout) != 0)
2937		perror_reply(550, "Data connection");
2938	else
2939		reply(226, "Transfer complete.");
2940
2941	transflag = 0;
2942	if (dout != NULL)
2943		(void) fclose(dout);
2944	data = -1;
2945	pdata = -1;
2946out:
2947	if (freeglob) {
2948		freeglob = 0;
2949		globfree(&gl);
2950	}
2951}
2952
2953void
2954reapchild(int signo)
2955{
2956	while (wait3(NULL, WNOHANG, NULL) > 0);
2957}
2958
2959#ifdef OLD_SETPROCTITLE
2960/*
2961 * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
2962 * Warning, since this is usually started from inetd.conf, it often doesn't
2963 * have much of an environment or arglist to overwrite.
2964 */
2965void
2966setproctitle(const char *fmt, ...)
2967{
2968	int i;
2969	va_list ap;
2970	char *p, *bp, ch;
2971	char buf[LINE_MAX];
2972
2973	va_start(ap, fmt);
2974	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
2975
2976	/* make ps print our process name */
2977	p = Argv[0];
2978	*p++ = '-';
2979
2980	i = strlen(buf);
2981	if (i > LastArgv - p - 2) {
2982		i = LastArgv - p - 2;
2983		buf[i] = '\0';
2984	}
2985	bp = buf;
2986	while (ch = *bp++)
2987		if (ch != '\n' && ch != '\r')
2988			*p++ = ch;
2989	while (p < LastArgv)
2990		*p++ = ' ';
2991}
2992#endif /* OLD_SETPROCTITLE */
2993
2994static void
2995logxfer(char *name, off_t size, time_t start)
2996{
2997	char buf[1024];
2998	char path[MAXPATHLEN + 1];
2999	time_t now;
3000
3001	if (statfd >= 0 && getwd(path) != NULL) {
3002		time(&now);
3003		snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%qd!%ld\n",
3004			ctime(&now)+4, ident, remotehost,
3005			path, name, (long long)size,
3006			(long)(now - start + (now == start)));
3007		write(statfd, buf, strlen(buf));
3008	}
3009}
3010
3011static char *
3012doublequote(char *s)
3013{
3014	int n;
3015	char *p, *s2;
3016
3017	for (p = s, n = 0; *p; p++)
3018		if (*p == '"')
3019			n++;
3020
3021	if ((s2 = malloc(p - s + n + 1)) == NULL)
3022		return (NULL);
3023
3024	for (p = s2; *s; s++, p++) {
3025		if ((*p = *s) == '"')
3026			*(++p) = '"';
3027	}
3028	*p = '\0';
3029
3030	return (s2);
3031}
3032