ftpd.c revision 100486
1189251Ssam/*
2189251Ssam * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
3189251Ssam *	The Regents of the University of California.  All rights reserved.
4189251Ssam *
5189251Ssam * Redistribution and use in source and binary forms, with or without
6189251Ssam * modification, are permitted provided that the following conditions
7189251Ssam * are met:
8189251Ssam * 1. Redistributions of source code must retain the above copyright
9189251Ssam *    notice, this list of conditions and the following disclaimer.
10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
11189251Ssam *    notice, this list of conditions and the following disclaimer in the
12189251Ssam *    documentation and/or other materials provided with the distribution.
13189251Ssam * 3. All advertising materials mentioning features or use of this software
14189251Ssam *    must display the following acknowledgement:
15189251Ssam *	This product includes software developed by the University of
16189251Ssam *	California, Berkeley and its contributors.
17189251Ssam * 4. Neither the name of the University nor the names of its contributors
18189251Ssam *    may be used to endorse or promote products derived from this software
19189251Ssam *    without specific prior written permission.
20189251Ssam *
21189251Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24189251Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31189251Ssam * SUCH DAMAGE.
32189251Ssam */
33189251Ssam
34189251Ssam#if 0
35189251Ssam#ifndef lint
36189251Ssamstatic char copyright[] =
37189251Ssam"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
38189251Ssam	The Regents of the University of California.  All rights reserved.\n";
39189251Ssam#endif /* not lint */
40189251Ssam#endif
41189251Ssam
42189251Ssam#ifndef lint
43189251Ssam#if 0
44189251Ssamstatic char sccsid[] = "@(#)ftpd.c	8.4 (Berkeley) 4/16/94";
45189251Ssam#endif
46189251Ssamstatic const char rcsid[] =
47189251Ssam  "$FreeBSD: head/libexec/ftpd/ftpd.c 100486 2002-07-22 07:41:14Z yar $";
48189251Ssam#endif /* not lint */
49189251Ssam
50189251Ssam/*
51189251Ssam * FTP server.
52189251Ssam */
53189251Ssam#include <sys/param.h>
54189251Ssam#include <sys/ioctl.h>
55189251Ssam#include <sys/mman.h>
56189251Ssam#include <sys/socket.h>
57189251Ssam#include <sys/stat.h>
58189251Ssam#include <sys/time.h>
59189251Ssam#include <sys/wait.h>
60189251Ssam
61189251Ssam#include <netinet/in.h>
62189251Ssam#include <netinet/in_systm.h>
63189251Ssam#include <netinet/ip.h>
64189251Ssam#include <netinet/tcp.h>
65189251Ssam
66189251Ssam#define	FTP_NAMES
67189251Ssam#include <arpa/ftp.h>
68189251Ssam#include <arpa/inet.h>
69189251Ssam#include <arpa/telnet.h>
70189251Ssam
71189251Ssam#include <ctype.h>
72189251Ssam#include <dirent.h>
73189251Ssam#include <err.h>
74189251Ssam#include <errno.h>
75189251Ssam#include <fcntl.h>
76189251Ssam#include <glob.h>
77189251Ssam#include <limits.h>
78189251Ssam#include <netdb.h>
79189251Ssam#include <pwd.h>
80189251Ssam#include <grp.h>
81189251Ssam#include <opie.h>
82189251Ssam#include <signal.h>
83189251Ssam#include <stdio.h>
84189251Ssam#include <stdlib.h>
85189251Ssam#include <string.h>
86189251Ssam#include <syslog.h>
87189251Ssam#include <time.h>
88189251Ssam#include <unistd.h>
89189251Ssam#include <libutil.h>
90189251Ssam#ifdef	LOGIN_CAP
91189251Ssam#include <login_cap.h>
92189251Ssam#endif
93189251Ssam
94189251Ssam#ifdef USE_PAM
95189251Ssam#include <security/pam_appl.h>
96189251Ssam#endif
97189251Ssam
98189251Ssam#include "pathnames.h"
99189251Ssam#include "extern.h"
100189251Ssam
101189251Ssam#include <stdarg.h>
102189251Ssam
103189251Ssamstatic char version[] = "Version 6.00LS";
104189251Ssam#undef main
105189251Ssam
106189251Ssamextern	off_t restart_point;
107189251Ssamextern	char cbuf[];
108189251Ssam
109189251Ssamunion sockunion server_addr;
110189251Ssamunion sockunion ctrl_addr;
111189251Ssamunion sockunion data_source;
112189251Ssamunion sockunion data_dest;
113189251Ssamunion sockunion his_addr;
114189251Ssamunion sockunion pasv_addr;
115189251Ssam
116189251Ssamint	daemon_mode;
117189251Ssamint	data;
118189251Ssamint	logged_in;
119189251Ssamstruct	passwd *pw;
120int	ftpdebug;
121int	timeout = 900;    /* timeout after 15 minutes of inactivity */
122int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
123int	logging;
124int	restricted_data_ports = 1;
125int	paranoid = 1;	  /* be extra careful about security */
126int	anon_only = 0;    /* Only anonymous ftp allowed */
127int	guest;
128int	dochroot;
129int	stats;
130int	statfd = -1;
131int	type;
132int	form;
133int	stru;			/* avoid C keyword */
134int	mode;
135int	usedefault = 1;		/* for data transfers */
136int	pdata = -1;		/* for passive mode */
137int	readonly=0;		/* Server is in readonly mode.	*/
138int	noepsv=0;		/* EPSV command is disabled.	*/
139int	noretr=0;		/* RETR command is disabled.	*/
140int	noguestretr=0;		/* RETR command is disabled for anon users. */
141int	noguestmkd=0;		/* MKD command is disabled for anon users. */
142
143static volatile sig_atomic_t recvurg;
144sig_atomic_t transflag;
145off_t	file_size;
146off_t	byte_count;
147#if !defined(CMASK) || CMASK == 0
148#undef CMASK
149#define CMASK 027
150#endif
151int	defumask = CMASK;		/* default umask value */
152char	tmpline[7];
153char	*hostname;
154int	epsvall = 0;
155
156#ifdef VIRTUAL_HOSTING
157char	*ftpuser;
158
159static struct ftphost {
160	struct ftphost	*next;
161	struct addrinfo *hostinfo;
162	char		*hostname;
163	char		*anonuser;
164	char		*statfile;
165	char		*welcome;
166	char		*loginmsg;
167} *thishost, *firsthost;
168
169#endif
170char	remotehost[MAXHOSTNAMELEN];
171char	*ident = NULL;
172
173static char ttyline[20];
174char	*tty = ttyline;		/* for klogin */
175
176#ifdef USE_PAM
177static int	auth_pam(struct passwd**, const char*);
178pam_handle_t *pamh = NULL;
179#endif
180
181static struct opie opiedata;
182static char opieprompt[OPIE_CHALLENGE_MAX+1];
183static int pwok;
184
185char	*pid_file = NULL;
186
187/*
188 * Limit number of pathnames that glob can return.
189 * A limit of 0 indicates the number of pathnames is unlimited.
190 */
191#define MAXGLOBARGS	16384
192#
193
194/*
195 * Timeout intervals for retrying connections
196 * to hosts that don't accept PORT cmds.  This
197 * is a kludge, but given the problems with TCP...
198 */
199#define	SWAITMAX	90	/* wait at most 90 seconds */
200#define	SWAITINT	5	/* interval between retries */
201
202int	swaitmax = SWAITMAX;
203int	swaitint = SWAITINT;
204
205#ifdef SETPROCTITLE
206#ifdef OLD_SETPROCTITLE
207char	**Argv = NULL;		/* pointer to argument vector */
208char	*LastArgv = NULL;	/* end of argv */
209#endif /* OLD_SETPROCTITLE */
210char	proctitle[LINE_MAX];	/* initial part of title */
211#endif /* SETPROCTITLE */
212
213#define LOGCMD(cmd, file) \
214	if (logging > 1) \
215	    syslog(LOG_INFO,"%s %s%s", cmd, \
216		*(file) == '/' ? "" : curdir(), file);
217#define LOGCMD2(cmd, file1, file2) \
218	 if (logging > 1) \
219	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
220		*(file1) == '/' ? "" : curdir(), file1, \
221		*(file2) == '/' ? "" : curdir(), file2);
222#define LOGBYTES(cmd, file, cnt) \
223	if (logging > 1) { \
224		if (cnt == (off_t)-1) \
225		    syslog(LOG_INFO,"%s %s%s", cmd, \
226			*(file) == '/' ? "" : curdir(), file); \
227		else \
228		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
229			cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \
230	}
231
232#ifdef VIRTUAL_HOSTING
233static void	 inithosts(void);
234static void	selecthost(union sockunion *);
235#endif
236static void	 ack(char *);
237static void	 sigurg(int);
238static void	 myoob(void);
239static int	 checkuser(char *, char *, int);
240static FILE	*dataconn(char *, off_t, char *);
241static void	 dolog(struct sockaddr *);
242static char	*curdir(void);
243static void	 end_login(void);
244static FILE	*getdatasock(char *);
245static char	*gunique(char *);
246static void	 lostconn(int);
247static void	 sigquit(int);
248static int	 receive_data(FILE *, FILE *);
249static int	 send_data(FILE *, FILE *, off_t, off_t, int);
250static struct passwd *
251		 sgetpwnam(char *);
252static char	*sgetsave(char *);
253static void	 reapchild(int);
254static void      logxfer(char *, off_t, time_t);
255static char	*doublequote(char *);
256
257static char *
258curdir(void)
259{
260	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
261
262	if (getcwd(path, sizeof(path)-2) == NULL)
263		return ("");
264	if (path[1] != '\0')		/* special case for root dir. */
265		strcat(path, "/");
266	/* For guest account, skip / since it's chrooted */
267	return (guest ? path+1 : path);
268}
269
270int
271main(int argc, char *argv[], char **envp)
272{
273	int addrlen, ch, on = 1, tos;
274	char *cp, line[LINE_MAX];
275	FILE *fd;
276	int error;
277	char	*bindname = NULL;
278	int	family = AF_UNSPEC;
279	int	enable_v4 = 0;
280	struct sigaction sa;
281
282	tzset();		/* in case no timezone database in ~ftp */
283	sigemptyset(&sa.sa_mask);
284	sa.sa_flags = SA_RESTART;
285
286#ifdef OLD_SETPROCTITLE
287	/*
288	 *  Save start and extent of argv for setproctitle.
289	 */
290	Argv = argv;
291	while (*envp)
292		envp++;
293	LastArgv = envp[-1] + strlen(envp[-1]);
294#endif /* OLD_SETPROCTITLE */
295
296
297	while ((ch = getopt(argc, argv, "AdlDESURrt:T:u:vMOoa:p:46")) != -1) {
298		switch (ch) {
299		case 'D':
300			daemon_mode++;
301			break;
302
303		case 'd':
304			ftpdebug++;
305			break;
306
307		case 'E':
308			noepsv = 1;
309			break;
310
311		case 'l':
312			logging++;	/* > 1 == extra logging */
313			break;
314
315		case 'r':
316			readonly = 1;
317			break;
318
319		case 'R':
320			paranoid = 0;
321			break;
322
323		case 'S':
324			stats++;
325			break;
326
327		case 'T':
328			maxtimeout = atoi(optarg);
329			if (timeout > maxtimeout)
330				timeout = maxtimeout;
331			break;
332
333		case 't':
334			timeout = atoi(optarg);
335			if (maxtimeout < timeout)
336				maxtimeout = timeout;
337			break;
338
339		case 'U':
340			restricted_data_ports = 0;
341			break;
342
343		case 'a':
344			bindname = optarg;
345			break;
346
347		case 'p':
348			pid_file = optarg;
349			break;
350
351		case 'u':
352		    {
353			long val = 0;
354
355			val = strtol(optarg, &optarg, 8);
356			if (*optarg != '\0' || val < 0)
357				warnx("bad value for -u");
358			else
359				defumask = val;
360			break;
361		    }
362		case 'A':
363			anon_only = 1;
364			break;
365
366		case 'v':
367			ftpdebug = 1;
368			break;
369
370		case '4':
371			enable_v4 = 1;
372			if (family == AF_UNSPEC)
373				family = AF_INET;
374			break;
375
376		case '6':
377			family = AF_INET6;
378			break;
379
380		case 'M':
381			noguestmkd = 1;
382			break;
383
384		case 'O':
385			noguestretr = 1;
386			break;
387
388		case 'o':
389			noretr = 1;
390			break;
391
392		default:
393			warnx("unknown flag -%c ignored", optopt);
394			break;
395		}
396	}
397
398#ifdef VIRTUAL_HOSTING
399	inithosts();
400#endif
401	(void) freopen(_PATH_DEVNULL, "w", stderr);
402
403	/*
404	 * LOG_NDELAY sets up the logging connection immediately,
405	 * necessary for anonymous ftp's that chroot and can't do it later.
406	 */
407	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
408
409	if (daemon_mode) {
410		int ctl_sock, fd;
411		struct addrinfo hints, *res;
412
413		/*
414		 * Detach from parent.
415		 */
416		if (daemon(1, 1) < 0) {
417			syslog(LOG_ERR, "failed to become a daemon");
418			exit(1);
419		}
420		sa.sa_handler = reapchild;
421		(void)sigaction(SIGCHLD, &sa, NULL);
422		/* init bind_sa */
423		memset(&hints, 0, sizeof(hints));
424
425		hints.ai_family = family == AF_UNSPEC ? AF_INET : family;
426		hints.ai_socktype = SOCK_STREAM;
427		hints.ai_protocol = 0;
428		hints.ai_flags = AI_PASSIVE;
429		error = getaddrinfo(bindname, "ftp", &hints, &res);
430		if (error) {
431			if (family == AF_UNSPEC) {
432				hints.ai_family = AF_UNSPEC;
433				error = getaddrinfo(bindname, "ftp", &hints,
434						    &res);
435			}
436		}
437		if (error) {
438			syslog(LOG_ERR, "%s", gai_strerror(error));
439			if (error == EAI_SYSTEM)
440				syslog(LOG_ERR, "%s", strerror(errno));
441			exit(1);
442		}
443		if (res->ai_addr == NULL) {
444			syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
445			exit(1);
446		} else
447			family = res->ai_addr->sa_family;
448		/*
449		 * Open a socket, bind it to the FTP port, and start
450		 * listening.
451		 */
452		ctl_sock = socket(family, SOCK_STREAM, 0);
453		if (ctl_sock < 0) {
454			syslog(LOG_ERR, "control socket: %m");
455			exit(1);
456		}
457		if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
458		    (char *)&on, sizeof(on)) < 0)
459			syslog(LOG_ERR, "control setsockopt: %m");
460#ifdef IPV6_BINDV6ONLY
461		if (family == AF_INET6 && enable_v4 == 0) {
462			if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY,
463				       (char *)&on, sizeof (on)) < 0)
464				syslog(LOG_ERR,
465				       "control setsockopt(IPV6_BINDV6ONLY): %m");
466		}
467#endif /* IPV6_BINDV6ONLY */
468		memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len);
469		if (bind(ctl_sock, (struct sockaddr *)&server_addr,
470			 server_addr.su_len) < 0) {
471			syslog(LOG_ERR, "control bind: %m");
472			exit(1);
473		}
474		if (listen(ctl_sock, 32) < 0) {
475			syslog(LOG_ERR, "control listen: %m");
476			exit(1);
477		}
478		/*
479		 * Atomically write process ID
480		 */
481		if (pid_file)
482		{
483			int fd;
484			char buf[20];
485
486			fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC
487				| O_NONBLOCK | O_EXLOCK, 0644);
488			if (fd < 0) {
489				if (errno == EAGAIN)
490					errx(1, "%s: file locked", pid_file);
491				else
492					err(1, "%s", pid_file);
493			}
494			snprintf(buf, sizeof(buf),
495				"%lu\n", (unsigned long) getpid());
496			if (write(fd, buf, strlen(buf)) < 0)
497				err(1, "%s: write", pid_file);
498			/* Leave the pid file open and locked */
499		}
500		/*
501		 * Loop forever accepting connection requests and forking off
502		 * children to handle them.
503		 */
504		while (1) {
505			addrlen = server_addr.su_len;
506			fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
507			if (fork() == 0) {
508				/* child */
509				(void) dup2(fd, 0);
510				(void) dup2(fd, 1);
511				close(ctl_sock);
512				break;
513			}
514			close(fd);
515		}
516	} else {
517		addrlen = sizeof(his_addr);
518		if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
519			syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
520			exit(1);
521		}
522	}
523
524	sa.sa_handler = SIG_DFL;
525	(void)sigaction(SIGCHLD, &sa, NULL);
526
527	sa.sa_handler = sigurg;
528	sa.sa_flags = 0;		/* don't restart syscalls for SIGURG */
529	(void)sigaction(SIGURG, &sa, NULL);
530
531	sigfillset(&sa.sa_mask);	/* block all signals in handler */
532	sa.sa_flags = SA_RESTART;
533	sa.sa_handler = sigquit;
534	(void)sigaction(SIGHUP, &sa, NULL);
535	(void)sigaction(SIGINT, &sa, NULL);
536	(void)sigaction(SIGQUIT, &sa, NULL);
537	(void)sigaction(SIGTERM, &sa, NULL);
538
539	sa.sa_handler = lostconn;
540	(void)sigaction(SIGPIPE, &sa, NULL);
541
542	addrlen = sizeof(ctrl_addr);
543	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
544		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
545		exit(1);
546	}
547#ifdef VIRTUAL_HOSTING
548	/* select our identity from virtual host table */
549	selecthost(&ctrl_addr);
550#endif
551#ifdef IP_TOS
552	if (ctrl_addr.su_family == AF_INET)
553      {
554	tos = IPTOS_LOWDELAY;
555	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
556		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
557      }
558#endif
559	/*
560	 * Disable Nagle on the control channel so that we don't have to wait
561	 * for peer's ACK before issuing our next reply.
562	 */
563	if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
564		syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m");
565
566	data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
567
568	/* set this here so klogin can use it... */
569	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
570
571	/* Try to handle urgent data inline */
572#ifdef SO_OOBINLINE
573	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
574		syslog(LOG_ERR, "setsockopt: %m");
575#endif
576
577#ifdef	F_SETOWN
578	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
579		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
580#endif
581	dolog((struct sockaddr *)&his_addr);
582	/*
583	 * Set up default state
584	 */
585	data = -1;
586	type = TYPE_A;
587	form = FORM_N;
588	stru = STRU_F;
589	mode = MODE_S;
590	tmpline[0] = '\0';
591
592	/* If logins are disabled, print out the message. */
593	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
594		while (fgets(line, sizeof(line), fd) != NULL) {
595			if ((cp = strchr(line, '\n')) != NULL)
596				*cp = '\0';
597			lreply(530, "%s", line);
598		}
599		(void) fflush(stdout);
600		(void) fclose(fd);
601		reply(530, "System not available.");
602		exit(0);
603	}
604#ifdef VIRTUAL_HOSTING
605	if ((fd = fopen(thishost->welcome, "r")) != NULL) {
606#else
607	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
608#endif
609		while (fgets(line, sizeof(line), fd) != NULL) {
610			if ((cp = strchr(line, '\n')) != NULL)
611				*cp = '\0';
612			lreply(220, "%s", line);
613		}
614		(void) fflush(stdout);
615		(void) fclose(fd);
616		/* reply(220,) must follow */
617	}
618#ifndef VIRTUAL_HOSTING
619	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
620		fatalerror("Ran out of memory.");
621	(void) gethostname(hostname, MAXHOSTNAMELEN - 1);
622	hostname[MAXHOSTNAMELEN - 1] = '\0';
623#endif
624	reply(220, "%s FTP server (%s) ready.", hostname, version);
625	for (;;)
626		(void) yyparse();
627	/* NOTREACHED */
628}
629
630static void
631lostconn(int signo)
632{
633
634	if (ftpdebug)
635		syslog(LOG_DEBUG, "lost connection");
636	dologout(1);
637}
638
639static void
640sigquit(int signo)
641{
642
643	syslog(LOG_ERR, "got signal %d", signo);
644	dologout(1);
645}
646
647#ifdef VIRTUAL_HOSTING
648/*
649 * read in virtual host tables (if they exist)
650 */
651
652static void
653inithosts(void)
654{
655	int insert;
656	size_t len;
657	FILE *fp;
658	char *cp, *mp, *line;
659	char *hostname;
660	char *vhost, *anonuser, *statfile, *welcome, *loginmsg;
661	struct ftphost *hrp, *lhrp;
662	struct addrinfo hints, *res, *ai;
663
664	/*
665	 * Fill in the default host information
666	 */
667	if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
668		fatalerror("Ran out of memory.");
669	if (gethostname(hostname, MAXHOSTNAMELEN) < 0)
670		hostname[0] = '\0';
671	hostname[MAXHOSTNAMELEN - 1] = '\0';
672	if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
673		fatalerror("Ran out of memory.");
674	hrp->hostname = hostname;
675	hrp->hostinfo = NULL;
676
677	memset(&hints, 0, sizeof(hints));
678	hints.ai_flags = AI_CANONNAME;
679	hints.ai_family = AF_UNSPEC;
680	getaddrinfo(hrp->hostname, NULL, &hints, &res);
681	if (res)
682		hrp->hostinfo = res;
683	hrp->statfile = _PATH_FTPDSTATFILE;
684	hrp->welcome  = _PATH_FTPWELCOME;
685	hrp->loginmsg = _PATH_FTPLOGINMESG;
686	hrp->anonuser = "ftp";
687	hrp->next = NULL;
688	thishost = firsthost = lhrp = hrp;
689	if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
690		int addrsize, error, gothost;
691		void *addr;
692		struct hostent *hp;
693
694		while ((line = fgetln(fp, &len)) != NULL) {
695			int	i, hp_error;
696
697			/* skip comments */
698			if (line[0] == '#')
699				continue;
700			if (line[len - 1] == '\n') {
701				line[len - 1] = '\0';
702				mp = NULL;
703			} else {
704				if ((mp = malloc(len + 1)) == NULL)
705					fatalerror("Ran out of memory.");
706				memcpy(mp, line, len);
707				mp[len] = '\0';
708				line = mp;
709			}
710			cp = strtok(line, " \t");
711			/* skip empty lines */
712			if (cp == NULL)
713				goto nextline;
714			vhost = cp;
715
716			/* set defaults */
717			anonuser = "ftp";
718			statfile = _PATH_FTPDSTATFILE;
719			welcome  = _PATH_FTPWELCOME;
720			loginmsg = _PATH_FTPLOGINMESG;
721
722			/*
723			 * Preparse the line so we can use its info
724			 * for all the addresses associated with
725			 * the virtual host name.
726			 * Field 0, the virtual host name, is special:
727			 * it's already parsed off and will be strdup'ed
728			 * later, after we know its canonical form.
729			 */
730			for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++)
731				if (*cp != '-' && (cp = strdup(cp)))
732					switch (i) {
733					case 1:	/* anon user permissions */
734						anonuser = cp;
735						break;
736					case 2: /* statistics file */
737						statfile = cp;
738						break;
739					case 3: /* welcome message */
740						welcome  = cp;
741						break;
742					case 4: /* login message */
743						loginmsg = cp;
744						break;
745					default: /* programming error */
746						abort();
747						/* NOTREACHED */
748					}
749
750			hints.ai_flags = 0;
751			hints.ai_family = AF_UNSPEC;
752			hints.ai_flags = AI_PASSIVE;
753			error = getaddrinfo(vhost, NULL, &hints, &res);
754			if (error != NULL)
755				goto nextline;
756			for (ai = res; ai != NULL && ai->ai_addr != NULL;
757			     ai = ai->ai_next) {
758
759			gothost = 0;
760			for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
761				struct addrinfo *hi;
762
763				for (hi = hrp->hostinfo; hi != NULL;
764				     hi = hi->ai_next)
765					if (hi->ai_addrlen == ai->ai_addrlen &&
766					    memcmp(hi->ai_addr,
767						   ai->ai_addr,
768						   ai->ai_addr->sa_len) == 0) {
769						gothost++;
770						break;
771					}
772				if (gothost)
773					break;
774			}
775			if (hrp == NULL) {
776				if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
777					goto nextline;
778				insert = 1;
779			} else
780				insert = 0; /* host already in the chain */
781			hrp->hostinfo = res;
782
783			/*
784			 * determine hostname to use.
785			 * force defined name if there is a valid alias
786			 * otherwise fallback to primary hostname
787			 */
788			/* XXX: getaddrinfo() can't do alias check */
789			switch(hrp->hostinfo->ai_family) {
790			case AF_INET:
791				addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr;
792				addrsize = sizeof(struct in_addr);
793				break;
794			case AF_INET6:
795				addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr;
796				addrsize = sizeof(struct in6_addr);
797				break;
798			default:
799				/* should not reach here */
800				if (hrp->hostinfo != NULL)
801					freeaddrinfo(hrp->hostinfo);
802				free(hrp);
803				goto nextline;
804				/* NOTREACHED */
805			}
806			if ((hp = getipnodebyaddr((char*)addr, addrsize,
807						  hrp->hostinfo->ai_family,
808						  &hp_error)) != NULL) {
809				if (strcmp(vhost, hp->h_name) != 0) {
810					if (hp->h_aliases == NULL)
811						vhost = hp->h_name;
812					else {
813						i = 0;
814						while (hp->h_aliases[i] &&
815						       strcmp(vhost, hp->h_aliases[i]) != 0)
816							++i;
817						if (hp->h_aliases[i] == NULL)
818							vhost = hp->h_name;
819					}
820				}
821			}
822			if ((hrp->hostname = strdup(vhost)) == NULL)
823				goto nextline;
824			hrp->anonuser = anonuser;
825			hrp->statfile = statfile;
826			hrp->welcome  = welcome;
827			hrp->loginmsg = loginmsg;
828			if (insert) {
829				hrp->next  = NULL;
830				lhrp->next = hrp;
831				lhrp = hrp;
832			}
833			if (hp)
834				freehostent(hp);
835		      }
836nextline:
837			if (mp)
838				free(mp);
839		}
840		(void) fclose(fp);
841	}
842}
843
844static void
845selecthost(union sockunion *su)
846{
847	struct ftphost	*hrp;
848	u_int16_t port;
849#ifdef INET6
850	struct in6_addr *mapped_in6 = NULL;
851#endif
852	struct addrinfo *hi;
853
854#ifdef INET6
855	/*
856	 * XXX IPv4 mapped IPv6 addr consideraton,
857	 * specified in rfc2373.
858	 */
859	if (su->su_family == AF_INET6 &&
860	    IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
861		mapped_in6 = &su->su_sin6.sin6_addr;
862#endif
863
864	hrp = thishost = firsthost;	/* default */
865	port = su->su_port;
866	su->su_port = 0;
867	while (hrp != NULL) {
868	    for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) {
869		if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) {
870			thishost = hrp;
871			break;
872		}
873#ifdef INET6
874		/* XXX IPv4 mapped IPv6 addr consideraton */
875		if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL &&
876		    (memcmp(&mapped_in6->s6_addr[12],
877			    &((struct sockaddr_in *)hi->ai_addr)->sin_addr,
878			    sizeof(struct in_addr)) == 0)) {
879			thishost = hrp;
880			break;
881		}
882#endif
883	    }
884	    hrp = hrp->next;
885	}
886	su->su_port = port;
887	/* setup static variables as appropriate */
888	hostname = thishost->hostname;
889	ftpuser = thishost->anonuser;
890}
891#endif
892
893/*
894 * Helper function for sgetpwnam().
895 */
896static char *
897sgetsave(char *s)
898{
899	char *new = malloc((unsigned) strlen(s) + 1);
900
901	if (new == NULL) {
902		perror_reply(421, "Local resource failure: malloc");
903		dologout(1);
904		/* NOTREACHED */
905	}
906	(void) strcpy(new, s);
907	return (new);
908}
909
910/*
911 * Save the result of a getpwnam.  Used for USER command, since
912 * the data returned must not be clobbered by any other command
913 * (e.g., globbing).
914 */
915static struct passwd *
916sgetpwnam(char *name)
917{
918	static struct passwd save;
919	struct passwd *p;
920
921	if ((p = getpwnam(name)) == NULL)
922		return (p);
923	if (save.pw_name) {
924		free(save.pw_name);
925		free(save.pw_passwd);
926		free(save.pw_gecos);
927		free(save.pw_dir);
928		free(save.pw_shell);
929	}
930	save = *p;
931	save.pw_name = sgetsave(p->pw_name);
932	save.pw_passwd = sgetsave(p->pw_passwd);
933	save.pw_gecos = sgetsave(p->pw_gecos);
934	save.pw_dir = sgetsave(p->pw_dir);
935	save.pw_shell = sgetsave(p->pw_shell);
936	return (&save);
937}
938
939static int login_attempts;	/* number of failed login attempts */
940static int askpasswd;		/* had user command, ask for passwd */
941static char curname[MAXLOGNAME];	/* current USER name */
942
943/*
944 * USER command.
945 * Sets global passwd pointer pw if named account exists and is acceptable;
946 * sets askpasswd if a PASS command is expected.  If logged in previously,
947 * need to reset state.  If name is "ftp" or "anonymous", the name is not in
948 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
949 * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
950 * requesting login privileges.  Disallow anyone who does not have a standard
951 * shell as returned by getusershell().  Disallow anyone mentioned in the file
952 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
953 */
954void
955user(char *name)
956{
957	char *cp, *shell;
958
959	if (logged_in) {
960		if (guest) {
961			reply(530, "Can't change user from guest login.");
962			return;
963		} else if (dochroot) {
964			reply(530, "Can't change user from chroot user.");
965			return;
966		}
967		end_login();
968	}
969
970	guest = 0;
971	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
972		if (checkuser(_PATH_FTPUSERS, "ftp", 0) ||
973		    checkuser(_PATH_FTPUSERS, "anonymous", 0))
974			reply(530, "User %s access denied.", name);
975#ifdef VIRTUAL_HOSTING
976		else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
977#else
978		else if ((pw = sgetpwnam("ftp")) != NULL) {
979#endif
980			guest = 1;
981			askpasswd = 1;
982			reply(331,
983			"Guest login ok, send your email address as password.");
984		} else
985			reply(530, "User %s unknown.", name);
986		if (!askpasswd && logging)
987			syslog(LOG_NOTICE,
988			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
989		return;
990	}
991	if (anon_only != 0) {
992		reply(530, "Sorry, only anonymous ftp allowed.");
993		return;
994	}
995
996	if ((pw = sgetpwnam(name))) {
997		if ((shell = pw->pw_shell) == NULL || *shell == 0)
998			shell = _PATH_BSHELL;
999		while ((cp = getusershell()) != NULL)
1000			if (strcmp(cp, shell) == 0)
1001				break;
1002		endusershell();
1003
1004		if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) {
1005			reply(530, "User %s access denied.", name);
1006			if (logging)
1007				syslog(LOG_NOTICE,
1008				    "FTP LOGIN REFUSED FROM %s, %s",
1009				    remotehost, name);
1010			pw = (struct passwd *) NULL;
1011			return;
1012		}
1013	}
1014	if (logging)
1015		strncpy(curname, name, sizeof(curname)-1);
1016
1017	pwok = 0;
1018#ifdef USE_PAM
1019	/* XXX Kluge! The conversation mechanism needs to be fixed. */
1020#endif
1021	if (opiechallenge(&opiedata, name, opieprompt) == 0) {
1022		pwok = (pw != NULL) &&
1023		       opieaccessfile(remotehost) &&
1024		       opiealways(pw->pw_dir);
1025		reply(331, "Response to %s %s for %s.",
1026		      opieprompt, pwok ? "requested" : "required", name);
1027	} else {
1028		pwok = 1;
1029		reply(331, "Password required for %s.", name);
1030	}
1031	askpasswd = 1;
1032	/*
1033	 * Delay before reading passwd after first failed
1034	 * attempt to slow down passwd-guessing programs.
1035	 */
1036	if (login_attempts)
1037		sleep((unsigned) login_attempts);
1038}
1039
1040/*
1041 * Check if a user is in the file "fname"
1042 */
1043static int
1044checkuser(char *fname, char *name, int pwset)
1045{
1046	FILE *fd;
1047	int found = 0;
1048	size_t len;
1049	char *line, *mp, *p;
1050
1051	if ((fd = fopen(fname, "r")) != NULL) {
1052		while (!found && (line = fgetln(fd, &len)) != NULL) {
1053			/* skip comments */
1054			if (line[0] == '#')
1055				continue;
1056			if (line[len - 1] == '\n') {
1057				line[len - 1] = '\0';
1058				mp = NULL;
1059			} else {
1060				if ((mp = malloc(len + 1)) == NULL)
1061					fatalerror("Ran out of memory.");
1062				memcpy(mp, line, len);
1063				mp[len] = '\0';
1064				line = mp;
1065			}
1066			/* avoid possible leading and trailing whitespace */
1067			p = strtok(line, " \t");
1068			/* skip empty lines */
1069			if (p == NULL)
1070				goto nextline;
1071			/*
1072			 * if first chr is '@', check group membership
1073			 */
1074			if (p[0] == '@') {
1075				int i = 0;
1076				struct group *grp;
1077
1078				if ((grp = getgrnam(p+1)) == NULL)
1079					goto nextline;
1080				/*
1081				 * Check user's default group
1082				 */
1083				if (pwset && grp->gr_gid == pw->pw_gid)
1084					found = 1;
1085				/*
1086				 * Check supplementary groups
1087				 */
1088				while (!found && grp->gr_mem[i])
1089					found = strcmp(name,
1090						grp->gr_mem[i++])
1091						== 0;
1092			}
1093			/*
1094			 * Otherwise, just check for username match
1095			 */
1096			else
1097				found = strcmp(p, name) == 0;
1098nextline:
1099			if (mp)
1100				free(mp);
1101		}
1102		(void) fclose(fd);
1103	}
1104	return (found);
1105}
1106
1107/*
1108 * Terminate login as previous user, if any, resetting state;
1109 * used when USER command is given or login fails.
1110 */
1111static void
1112end_login(void)
1113{
1114#ifdef USE_PAM
1115	int e;
1116#endif
1117
1118	(void) seteuid((uid_t)0);
1119	if (logged_in)
1120		ftpd_logwtmp(ttyline, "", NULL);
1121	pw = NULL;
1122#ifdef	LOGIN_CAP
1123	setusercontext(NULL, getpwuid(0), (uid_t)0,
1124		       LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1125#endif
1126#ifdef USE_PAM
1127	if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
1128		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
1129	if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
1130		syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e));
1131	if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
1132		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1133	pamh = NULL;
1134#endif
1135	logged_in = 0;
1136	guest = 0;
1137	dochroot = 0;
1138}
1139
1140#ifdef USE_PAM
1141
1142/*
1143 * the following code is stolen from imap-uw PAM authentication module and
1144 * login.c
1145 */
1146#define COPY_STRING(s) (s ? strdup(s) : NULL)
1147
1148struct cred_t {
1149	const char *uname;		/* user name */
1150	const char *pass;		/* password */
1151};
1152typedef struct cred_t cred_t;
1153
1154static int
1155auth_conv(int num_msg, const struct pam_message **msg,
1156	  struct pam_response **resp, void *appdata)
1157{
1158	int i;
1159	cred_t *cred = (cred_t *) appdata;
1160	struct pam_response *reply;
1161
1162	reply = calloc(num_msg, sizeof *reply);
1163	if (reply == NULL)
1164		return PAM_BUF_ERR;
1165
1166	for (i = 0; i < num_msg; i++) {
1167		switch (msg[i]->msg_style) {
1168		case PAM_PROMPT_ECHO_ON:	/* assume want user name */
1169			reply[i].resp_retcode = PAM_SUCCESS;
1170			reply[i].resp = COPY_STRING(cred->uname);
1171			/* PAM frees resp. */
1172			break;
1173		case PAM_PROMPT_ECHO_OFF:	/* assume want password */
1174			reply[i].resp_retcode = PAM_SUCCESS;
1175			reply[i].resp = COPY_STRING(cred->pass);
1176			/* PAM frees resp. */
1177			break;
1178		case PAM_TEXT_INFO:
1179		case PAM_ERROR_MSG:
1180			reply[i].resp_retcode = PAM_SUCCESS;
1181			reply[i].resp = NULL;
1182			break;
1183		default:			/* unknown message style */
1184			free(reply);
1185			return PAM_CONV_ERR;
1186		}
1187	}
1188
1189	*resp = reply;
1190	return PAM_SUCCESS;
1191}
1192
1193/*
1194 * Attempt to authenticate the user using PAM.  Returns 0 if the user is
1195 * authenticated, or 1 if not authenticated.  If some sort of PAM system
1196 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
1197 * function returns -1.  This can be used as an indication that we should
1198 * fall back to a different authentication mechanism.
1199 */
1200static int
1201auth_pam(struct passwd **ppw, const char *pass)
1202{
1203	pam_handle_t *pamh = NULL;
1204	const char *tmpl_user;
1205	const void *item;
1206	int rval;
1207	int e;
1208	cred_t auth_cred = { (*ppw)->pw_name, pass };
1209	struct pam_conv conv = { &auth_conv, &auth_cred };
1210
1211	e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh);
1212	if (e != PAM_SUCCESS) {
1213		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
1214		return -1;
1215	}
1216
1217	e = pam_set_item(pamh, PAM_RHOST, remotehost);
1218	if (e != PAM_SUCCESS) {
1219		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
1220			pam_strerror(pamh, e));
1221		return -1;
1222	}
1223
1224	e = pam_authenticate(pamh, 0);
1225	switch (e) {
1226	case PAM_SUCCESS:
1227		/*
1228		 * With PAM we support the concept of a "template"
1229		 * user.  The user enters a login name which is
1230		 * authenticated by PAM, usually via a remote service
1231		 * such as RADIUS or TACACS+.  If authentication
1232		 * succeeds, a different but related "template" name
1233		 * is used for setting the credentials, shell, and
1234		 * home directory.  The name the user enters need only
1235		 * exist on the remote authentication server, but the
1236		 * template name must be present in the local password
1237		 * database.
1238		 *
1239		 * This is supported by two various mechanisms in the
1240		 * individual modules.  However, from the application's
1241		 * point of view, the template user is always passed
1242		 * back as a changed value of the PAM_USER item.
1243		 */
1244		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
1245		    PAM_SUCCESS) {
1246			tmpl_user = (const char *) item;
1247			if (strcmp((*ppw)->pw_name, tmpl_user) != 0)
1248				*ppw = getpwnam(tmpl_user);
1249		} else
1250			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
1251			    pam_strerror(pamh, e));
1252		rval = 0;
1253		break;
1254
1255	case PAM_AUTH_ERR:
1256	case PAM_USER_UNKNOWN:
1257	case PAM_MAXTRIES:
1258		rval = 1;
1259		break;
1260
1261	default:
1262		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
1263		rval = -1;
1264		break;
1265	}
1266
1267	if (rval == 0) {
1268		e = pam_acct_mgmt(pamh, 0);
1269		if (e == PAM_NEW_AUTHTOK_REQD) {
1270			e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
1271			if (e != PAM_SUCCESS) {
1272				syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e));
1273				rval = 1;
1274			}
1275		} else if (e != PAM_SUCCESS) {
1276			rval = 1;
1277		}
1278	}
1279
1280	if (rval != 0) {
1281		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
1282			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
1283		}
1284		pamh = NULL;
1285	}
1286	return rval;
1287}
1288
1289#endif /* USE_PAM */
1290
1291void
1292pass(char *passwd)
1293{
1294	int rval;
1295	FILE *fd;
1296#ifdef	LOGIN_CAP
1297	login_cap_t *lc = NULL;
1298#endif
1299#ifdef USE_PAM
1300	int e;
1301#endif
1302	char *xpasswd;
1303
1304	if (logged_in || askpasswd == 0) {
1305		reply(503, "Login with USER first.");
1306		return;
1307	}
1308	askpasswd = 0;
1309	if (!guest) {		/* "ftp" is only account allowed no password */
1310		if (pw == NULL) {
1311			rval = 1;	/* failure below */
1312			goto skip;
1313		}
1314#ifdef USE_PAM
1315		rval = auth_pam(&pw, passwd);
1316		if (rval >= 0) {
1317			opieunlock();
1318			goto skip;
1319		}
1320#endif
1321		if (opieverify(&opiedata, passwd) == 0)
1322			xpasswd = pw->pw_passwd;
1323		else if (pwok) {
1324			xpasswd = crypt(passwd, pw->pw_passwd);
1325			if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0')
1326				xpasswd = ":";
1327		} else {
1328			rval = 1;
1329			goto skip;
1330		}
1331		rval = strcmp(pw->pw_passwd, xpasswd);
1332		if (pw->pw_expire && time(NULL) >= pw->pw_expire)
1333			rval = 1;	/* failure */
1334skip:
1335		/*
1336		 * If rval == 1, the user failed the authentication check
1337		 * above.  If rval == 0, either PAM or local authentication
1338		 * succeeded.
1339		 */
1340		if (rval) {
1341			reply(530, "Login incorrect.");
1342			if (logging)
1343				syslog(LOG_NOTICE,
1344				    "FTP LOGIN FAILED FROM %s, %s",
1345				    remotehost, curname);
1346			pw = NULL;
1347			if (login_attempts++ >= 5) {
1348				syslog(LOG_NOTICE,
1349				    "repeated login failures from %s",
1350				    remotehost);
1351				exit(0);
1352			}
1353			return;
1354		}
1355	}
1356	login_attempts = 0;		/* this time successful */
1357	if (setegid((gid_t)pw->pw_gid) < 0) {
1358		reply(550, "Can't set gid.");
1359		return;
1360	}
1361	/* May be overridden by login.conf */
1362	(void) umask(defumask);
1363#ifdef	LOGIN_CAP
1364	if ((lc = login_getpwclass(pw)) != NULL) {
1365		char	remote_ip[MAXHOSTNAMELEN];
1366
1367		getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1368			remote_ip, sizeof(remote_ip) - 1, NULL, 0,
1369			NI_NUMERICHOST);
1370		remote_ip[sizeof(remote_ip) - 1] = 0;
1371		if (!auth_hostok(lc, remotehost, remote_ip)) {
1372			syslog(LOG_INFO|LOG_AUTH,
1373			    "FTP LOGIN FAILED (HOST) as %s: permission denied.",
1374			    pw->pw_name);
1375			reply(530, "Permission denied.\n");
1376			pw = NULL;
1377			return;
1378		}
1379		if (!auth_timeok(lc, time(NULL))) {
1380			reply(530, "Login not available right now.\n");
1381			pw = NULL;
1382			return;
1383		}
1384	}
1385	setusercontext(lc, pw, (uid_t)0,
1386		LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY|
1387		LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1388#else
1389	setlogin(pw->pw_name);
1390	(void) initgroups(pw->pw_name, pw->pw_gid);
1391#endif
1392
1393#ifdef USE_PAM
1394	if (pamh) {
1395		if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
1396			syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
1397		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1398			syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
1399		}
1400	}
1401#endif
1402
1403	/* open wtmp before chroot */
1404	ftpd_logwtmp(ttyline, pw->pw_name, (struct sockaddr *)&his_addr);
1405	logged_in = 1;
1406
1407	if (guest && stats && statfd < 0)
1408#ifdef VIRTUAL_HOSTING
1409		if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0)
1410#else
1411		if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
1412#endif
1413			stats = 0;
1414
1415	dochroot =
1416#ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
1417		login_getcapbool(lc, "ftp-chroot", 0) ||
1418#endif
1419		checkuser(_PATH_FTPCHROOT, pw->pw_name, 1);
1420	if (guest) {
1421		/*
1422		 * We MUST do a chdir() after the chroot. Otherwise
1423		 * the old current directory will be accessible as "."
1424		 * outside the new root!
1425		 */
1426		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
1427			reply(550, "Can't set guest privileges.");
1428			goto bad;
1429		}
1430	} else if (dochroot) {
1431		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
1432			reply(550, "Can't change root.");
1433			goto bad;
1434		}
1435	} else if (chdir(pw->pw_dir) < 0) {
1436		if (chdir("/") < 0) {
1437			reply(530, "User %s: can't change directory to %s.",
1438			    pw->pw_name, pw->pw_dir);
1439			goto bad;
1440		} else
1441			lreply(230, "No directory! Logging in with home=/");
1442	}
1443	if (seteuid((uid_t)pw->pw_uid) < 0) {
1444		reply(550, "Can't set uid.");
1445		goto bad;
1446	}
1447
1448	/*
1449	 * Display a login message, if it exists.
1450	 * N.B. reply(230,) must follow the message.
1451	 */
1452#ifdef VIRTUAL_HOSTING
1453	if ((fd = fopen(thishost->loginmsg, "r")) != NULL) {
1454#else
1455	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
1456#endif
1457		char *cp, line[LINE_MAX];
1458
1459		while (fgets(line, sizeof(line), fd) != NULL) {
1460			if ((cp = strchr(line, '\n')) != NULL)
1461				*cp = '\0';
1462			lreply(230, "%s", line);
1463		}
1464		(void) fflush(stdout);
1465		(void) fclose(fd);
1466	}
1467	if (guest) {
1468		if (ident != NULL)
1469			free(ident);
1470		ident = strdup(passwd);
1471		if (ident == NULL)
1472			fatalerror("Ran out of memory.");
1473
1474		reply(230, "Guest login ok, access restrictions apply.");
1475#ifdef SETPROCTITLE
1476#ifdef VIRTUAL_HOSTING
1477		if (thishost != firsthost)
1478			snprintf(proctitle, sizeof(proctitle),
1479				 "%s: anonymous(%s)/%s", remotehost, hostname,
1480				 passwd);
1481		else
1482#endif
1483			snprintf(proctitle, sizeof(proctitle),
1484				 "%s: anonymous/%s", remotehost, passwd);
1485		setproctitle("%s", proctitle);
1486#endif /* SETPROCTITLE */
1487		if (logging)
1488			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
1489			    remotehost, passwd);
1490	} else {
1491		if (dochroot)
1492			reply(230, "User %s logged in, "
1493				   "access restrictions apply.", pw->pw_name);
1494		else
1495			reply(230, "User %s logged in.", pw->pw_name);
1496
1497#ifdef SETPROCTITLE
1498		snprintf(proctitle, sizeof(proctitle),
1499			 "%s: user/%s", remotehost, pw->pw_name);
1500		setproctitle("%s", proctitle);
1501#endif /* SETPROCTITLE */
1502		if (logging)
1503			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
1504			    remotehost, pw->pw_name);
1505	}
1506#ifdef	LOGIN_CAP
1507	login_close(lc);
1508#endif
1509	return;
1510bad:
1511	/* Forget all about it... */
1512#ifdef	LOGIN_CAP
1513	login_close(lc);
1514#endif
1515	end_login();
1516}
1517
1518void
1519retrieve(char *cmd, char *name)
1520{
1521	FILE *fin, *dout;
1522	struct stat st;
1523	int (*closefunc)(FILE *);
1524	time_t start;
1525
1526	if (cmd == 0) {
1527		fin = fopen(name, "r"), closefunc = fclose;
1528		st.st_size = 0;
1529	} else {
1530		char line[BUFSIZ];
1531
1532		(void) snprintf(line, sizeof(line), cmd, name), name = line;
1533		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
1534		st.st_size = -1;
1535		st.st_blksize = BUFSIZ;
1536	}
1537	if (fin == NULL) {
1538		if (errno != 0) {
1539			perror_reply(550, name);
1540			if (cmd == 0) {
1541				LOGCMD("get", name);
1542			}
1543		}
1544		return;
1545	}
1546	byte_count = -1;
1547	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
1548		reply(550, "%s: not a plain file.", name);
1549		goto done;
1550	}
1551	if (restart_point) {
1552		if (type == TYPE_A) {
1553			off_t i, n;
1554			int c;
1555
1556			n = restart_point;
1557			i = 0;
1558			while (i++ < n) {
1559				if ((c=getc(fin)) == EOF) {
1560					perror_reply(550, name);
1561					goto done;
1562				}
1563				if (c == '\n')
1564					i++;
1565			}
1566		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
1567			perror_reply(550, name);
1568			goto done;
1569		}
1570	}
1571	dout = dataconn(name, st.st_size, "w");
1572	if (dout == NULL)
1573		goto done;
1574	time(&start);
1575	send_data(fin, dout, st.st_blksize, st.st_size,
1576		  restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
1577	if (cmd == 0 && guest && stats)
1578		logxfer(name, st.st_size, start);
1579	(void) fclose(dout);
1580	data = -1;
1581	pdata = -1;
1582done:
1583	if (cmd == 0)
1584		LOGBYTES("get", name, byte_count);
1585	(*closefunc)(fin);
1586}
1587
1588void
1589store(char *name, char *mode, int unique)
1590{
1591	FILE *fout, *din;
1592	struct stat st;
1593	int (*closefunc)(FILE *);
1594
1595	if ((unique || guest) && stat(name, &st) == 0 &&
1596	    (name = gunique(name)) == NULL) {
1597		LOGCMD(*mode == 'w' ? "put" : "append", name);
1598		return;
1599	}
1600
1601	if (restart_point)
1602		mode = "r+";
1603	fout = fopen(name, mode);
1604	closefunc = fclose;
1605	if (fout == NULL) {
1606		perror_reply(553, name);
1607		LOGCMD(*mode == 'w' ? "put" : "append", name);
1608		return;
1609	}
1610	byte_count = -1;
1611	if (restart_point) {
1612		if (type == TYPE_A) {
1613			off_t i, n;
1614			int c;
1615
1616			n = restart_point;
1617			i = 0;
1618			while (i++ < n) {
1619				if ((c=getc(fout)) == EOF) {
1620					perror_reply(550, name);
1621					goto done;
1622				}
1623				if (c == '\n')
1624					i++;
1625			}
1626			/*
1627			 * We must do this seek to "current" position
1628			 * because we are changing from reading to
1629			 * writing.
1630			 */
1631			if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) {
1632				perror_reply(550, name);
1633				goto done;
1634			}
1635		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
1636			perror_reply(550, name);
1637			goto done;
1638		}
1639	}
1640	din = dataconn(name, (off_t)-1, "r");
1641	if (din == NULL)
1642		goto done;
1643	if (receive_data(din, fout) == 0) {
1644		if (unique)
1645			reply(226, "Transfer complete (unique file name:%s).",
1646			    name);
1647		else
1648			reply(226, "Transfer complete.");
1649	}
1650	(void) fclose(din);
1651	data = -1;
1652	pdata = -1;
1653done:
1654	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
1655	(*closefunc)(fout);
1656}
1657
1658static FILE *
1659getdatasock(char *mode)
1660{
1661	int on = 1, s, t, tries;
1662
1663	if (data >= 0)
1664		return (fdopen(data, mode));
1665	(void) seteuid((uid_t)0);
1666
1667	s = socket(data_dest.su_family, SOCK_STREAM, 0);
1668	if (s < 0)
1669		goto bad;
1670	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1671	    (char *) &on, sizeof(on)) < 0)
1672		goto bad;
1673	/* anchor socket to avoid multi-homing problems */
1674	data_source = ctrl_addr;
1675	data_source.su_port = htons(20); /* ftp-data port */
1676	for (tries = 1; ; tries++) {
1677		if (bind(s, (struct sockaddr *)&data_source,
1678		    data_source.su_len) >= 0)
1679			break;
1680		if (errno != EADDRINUSE || tries > 10)
1681			goto bad;
1682		sleep(tries);
1683	}
1684	(void) seteuid((uid_t)pw->pw_uid);
1685#ifdef IP_TOS
1686	if (data_source.su_family == AF_INET)
1687      {
1688	on = IPTOS_THROUGHPUT;
1689	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
1690		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1691      }
1692#endif
1693#ifdef TCP_NOPUSH
1694	/*
1695	 * Turn off push flag to keep sender TCP from sending short packets
1696	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
1697	 * to set the send buffer size as well, but that may not be desirable
1698	 * in heavy-load situations.
1699	 */
1700	on = 1;
1701	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
1702		syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
1703#endif
1704#ifdef SO_SNDBUF
1705	on = 65536;
1706	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
1707		syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
1708#endif
1709
1710	return (fdopen(s, mode));
1711bad:
1712	/* Return the real value of errno (close may change it) */
1713	t = errno;
1714	(void) seteuid((uid_t)pw->pw_uid);
1715	(void) close(s);
1716	errno = t;
1717	return (NULL);
1718}
1719
1720static FILE *
1721dataconn(char *name, off_t size, char *mode)
1722{
1723	char sizebuf[32];
1724	FILE *file;
1725	int retry = 0, tos;
1726
1727	file_size = size;
1728	byte_count = 0;
1729	if (size != (off_t) -1)
1730		(void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size);
1731	else
1732		*sizebuf = '\0';
1733	if (pdata >= 0) {
1734		union sockunion from;
1735		int flags;
1736		int s, fromlen = ctrl_addr.su_len;
1737		struct timeval timeout;
1738		fd_set set;
1739
1740		FD_ZERO(&set);
1741		FD_SET(pdata, &set);
1742
1743		timeout.tv_usec = 0;
1744		timeout.tv_sec = 120;
1745
1746		/*
1747		 * Granted a socket is in the blocking I/O mode,
1748		 * accept() will block after a successful select()
1749		 * if the selected connection dies in between.
1750		 * Therefore set the non-blocking I/O flag here.
1751		 */
1752		if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
1753		    fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1)
1754			goto pdata_err;
1755		if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) <= 0 ||
1756		    (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0)
1757			goto pdata_err;
1758		(void) close(pdata);
1759		pdata = s;
1760		/*
1761		 * Unset the blocking I/O flag on the child socket
1762		 * again so stdio can work on it.
1763		 */
1764		if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
1765		    fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1)
1766			goto pdata_err;
1767#ifdef IP_TOS
1768		if (from.su_family == AF_INET)
1769	      {
1770		tos = IPTOS_THROUGHPUT;
1771		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
1772		    sizeof(int));
1773	      }
1774#endif
1775		reply(150, "Opening %s mode data connection for '%s'%s.",
1776		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1777		return (fdopen(pdata, mode));
1778pdata_err:
1779		reply(425, "Can't open data connection.");
1780		(void) close(pdata);
1781		pdata = -1;
1782		return (NULL);
1783	}
1784	if (data >= 0) {
1785		reply(125, "Using existing data connection for '%s'%s.",
1786		    name, sizebuf);
1787		usedefault = 1;
1788		return (fdopen(data, mode));
1789	}
1790	if (usedefault)
1791		data_dest = his_addr;
1792	usedefault = 1;
1793	file = getdatasock(mode);
1794	if (file == NULL) {
1795		char hostbuf[BUFSIZ], portbuf[BUFSIZ];
1796		getnameinfo((struct sockaddr *)&data_source,
1797			data_source.su_len, hostbuf, sizeof(hostbuf) - 1,
1798			portbuf, sizeof(portbuf),
1799			NI_NUMERICHOST|NI_NUMERICSERV);
1800		reply(425, "Can't create data socket (%s,%s): %s.",
1801			hostbuf, portbuf, strerror(errno));
1802		return (NULL);
1803	}
1804	data = fileno(file);
1805	while (connect(data, (struct sockaddr *)&data_dest,
1806	    data_dest.su_len) < 0) {
1807		if (errno == EADDRINUSE && retry < swaitmax) {
1808			sleep((unsigned) swaitint);
1809			retry += swaitint;
1810			continue;
1811		}
1812		perror_reply(425, "Can't build data connection");
1813		(void) fclose(file);
1814		data = -1;
1815		return (NULL);
1816	}
1817	reply(150, "Opening %s mode data connection for '%s'%s.",
1818	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
1819	return (file);
1820}
1821
1822/*
1823 * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1824 * encapsulation of the data subject to Mode, Structure, and Type.
1825 *
1826 * NB: Form isn't handled.
1827 */
1828static int
1829send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
1830{
1831	int c, filefd, netfd;
1832	char *buf;
1833	off_t cnt;
1834
1835	transflag++;
1836	switch (type) {
1837
1838	case TYPE_A:
1839		while ((c = getc(instr)) != EOF) {
1840			if (recvurg)
1841				goto got_oob;
1842			byte_count++;
1843			if (c == '\n') {
1844				if (ferror(outstr))
1845					goto data_err;
1846				(void) putc('\r', outstr);
1847			}
1848			(void) putc(c, outstr);
1849		}
1850		if (recvurg)
1851			goto got_oob;
1852		fflush(outstr);
1853		transflag = 0;
1854		if (ferror(instr))
1855			goto file_err;
1856		if (ferror(outstr))
1857			goto data_err;
1858		reply(226, "Transfer complete.");
1859		return (0);
1860
1861	case TYPE_I:
1862	case TYPE_L:
1863		/*
1864		 * isreg is only set if we are not doing restart and we
1865		 * are sending a regular file
1866		 */
1867		netfd = fileno(outstr);
1868		filefd = fileno(instr);
1869
1870		if (isreg) {
1871
1872			off_t offset;
1873			int err;
1874
1875			err = cnt = offset = 0;
1876
1877			while (err != -1 && filesize > 0) {
1878				err = sendfile(filefd, netfd, offset, 0,
1879					(struct sf_hdtr *) NULL, &cnt, 0);
1880				/*
1881				 * Calculate byte_count before OOB processing.
1882				 * It can be used in myoob() later.
1883				 */
1884				byte_count += cnt;
1885				if (recvurg)
1886					goto got_oob;
1887				offset += cnt;
1888				filesize -= cnt;
1889
1890				if (err == -1) {
1891					if (!cnt)
1892						goto oldway;
1893
1894					goto data_err;
1895				}
1896			}
1897
1898			transflag = 0;
1899			reply(226, "Transfer complete.");
1900			return (0);
1901		}
1902
1903oldway:
1904		if ((buf = malloc((u_int)blksize)) == NULL) {
1905			transflag = 0;
1906			perror_reply(451, "Local resource failure: malloc");
1907			return (-1);
1908		}
1909
1910		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1911		    write(netfd, buf, cnt) == cnt)
1912			byte_count += cnt;
1913		transflag = 0;
1914		(void)free(buf);
1915		if (cnt != 0) {
1916			if (cnt < 0)
1917				goto file_err;
1918			goto data_err;
1919		}
1920		reply(226, "Transfer complete.");
1921		return (0);
1922	default:
1923		transflag = 0;
1924		reply(550, "Unimplemented TYPE %d in send_data", type);
1925		return (-1);
1926	}
1927
1928data_err:
1929	transflag = 0;
1930	perror_reply(426, "Data connection");
1931	return (-1);
1932
1933file_err:
1934	transflag = 0;
1935	perror_reply(551, "Error on input file");
1936	return (-1);
1937
1938got_oob:
1939	myoob();
1940	recvurg = 0;
1941	transflag = 0;
1942	return (-1);
1943}
1944
1945/*
1946 * Transfer data from peer to "outstr" using the appropriate encapulation of
1947 * the data subject to Mode, Structure, and Type.
1948 *
1949 * N.B.: Form isn't handled.
1950 */
1951static int
1952receive_data(FILE *instr, FILE *outstr)
1953{
1954	int c;
1955	int cnt, bare_lfs;
1956	char buf[BUFSIZ];
1957
1958	transflag++;
1959	bare_lfs = 0;
1960
1961	switch (type) {
1962
1963	case TYPE_I:
1964	case TYPE_L:
1965		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1966			if (recvurg)
1967				goto got_oob;
1968			if (write(fileno(outstr), buf, cnt) != cnt)
1969				goto file_err;
1970			byte_count += cnt;
1971		}
1972		if (recvurg)
1973			goto got_oob;
1974		if (cnt < 0)
1975			goto data_err;
1976		transflag = 0;
1977		return (0);
1978
1979	case TYPE_E:
1980		reply(553, "TYPE E not implemented.");
1981		transflag = 0;
1982		return (-1);
1983
1984	case TYPE_A:
1985		while ((c = getc(instr)) != EOF) {
1986			if (recvurg)
1987				goto got_oob;
1988			byte_count++;
1989			if (c == '\n')
1990				bare_lfs++;
1991			while (c == '\r') {
1992				if (ferror(outstr))
1993					goto data_err;
1994				if ((c = getc(instr)) != '\n') {
1995					(void) putc ('\r', outstr);
1996					if (c == '\0' || c == EOF)
1997						goto contin2;
1998				}
1999			}
2000			(void) putc(c, outstr);
2001	contin2:	;
2002		}
2003		if (recvurg)
2004			goto got_oob;
2005		fflush(outstr);
2006		if (ferror(instr))
2007			goto data_err;
2008		if (ferror(outstr))
2009			goto file_err;
2010		transflag = 0;
2011		if (bare_lfs) {
2012			lreply(226,
2013		"WARNING! %d bare linefeeds received in ASCII mode",
2014			    bare_lfs);
2015		(void)printf("   File may not have transferred correctly.\r\n");
2016		}
2017		return (0);
2018	default:
2019		reply(550, "Unimplemented TYPE %d in receive_data", type);
2020		transflag = 0;
2021		return (-1);
2022	}
2023
2024data_err:
2025	transflag = 0;
2026	perror_reply(426, "Data Connection");
2027	return (-1);
2028
2029file_err:
2030	transflag = 0;
2031	perror_reply(452, "Error writing file");
2032	return (-1);
2033
2034got_oob:
2035	myoob();
2036	recvurg = 0;
2037	transflag = 0;
2038	return (-1);
2039}
2040
2041void
2042statfilecmd(char *filename)
2043{
2044	FILE *fin;
2045	int c;
2046	char line[LINE_MAX];
2047
2048	(void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename);
2049	fin = ftpd_popen(line, "r");
2050	lreply(211, "status of %s:", filename);
2051	while ((c = getc(fin)) != EOF) {
2052		if (c == '\n') {
2053			if (ferror(stdout)){
2054				perror_reply(421, "control connection");
2055				(void) ftpd_pclose(fin);
2056				dologout(1);
2057				/* NOTREACHED */
2058			}
2059			if (ferror(fin)) {
2060				perror_reply(551, filename);
2061				(void) ftpd_pclose(fin);
2062				return;
2063			}
2064			(void) putc('\r', stdout);
2065		}
2066		(void) putc(c, stdout);
2067	}
2068	(void) ftpd_pclose(fin);
2069	reply(211, "End of Status");
2070}
2071
2072void
2073statcmd(void)
2074{
2075	union sockunion *su;
2076	u_char *a, *p;
2077	char hname[NI_MAXHOST];
2078	int ispassive;
2079
2080	lreply(211, "%s FTP server status:", hostname, version);
2081	printf("     %s\r\n", version);
2082	printf("     Connected to %s", remotehost);
2083	if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
2084			 hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) {
2085		if (strcmp(hname, remotehost) != 0)
2086			printf(" (%s)", hname);
2087	}
2088	printf("\r\n");
2089	if (logged_in) {
2090		if (guest)
2091			printf("     Logged in anonymously\r\n");
2092		else
2093			printf("     Logged in as %s\r\n", pw->pw_name);
2094	} else if (askpasswd)
2095		printf("     Waiting for password\r\n");
2096	else
2097		printf("     Waiting for user name\r\n");
2098	printf("     TYPE: %s", typenames[type]);
2099	if (type == TYPE_A || type == TYPE_E)
2100		printf(", FORM: %s", formnames[form]);
2101	if (type == TYPE_L)
2102#if NBBY == 8
2103		printf(" %d", NBBY);
2104#else
2105		printf(" %d", bytesize);	/* need definition! */
2106#endif
2107	printf("; STRUcture: %s; transfer MODE: %s\r\n",
2108	    strunames[stru], modenames[mode]);
2109	if (data != -1)
2110		printf("     Data connection open\r\n");
2111	else if (pdata != -1) {
2112		ispassive = 1;
2113		su = &pasv_addr;
2114		goto printaddr;
2115	} else if (usedefault == 0) {
2116		ispassive = 0;
2117		su = &data_dest;
2118printaddr:
2119#define UC(b) (((int) b) & 0xff)
2120		if (epsvall) {
2121			printf("     EPSV only mode (EPSV ALL)\r\n");
2122			goto epsvonly;
2123		}
2124
2125		/* PORT/PASV */
2126		if (su->su_family == AF_INET) {
2127			a = (u_char *) &su->su_sin.sin_addr;
2128			p = (u_char *) &su->su_sin.sin_port;
2129			printf("     %s (%d,%d,%d,%d,%d,%d)\r\n",
2130				ispassive ? "PASV" : "PORT",
2131				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2132				UC(p[0]), UC(p[1]));
2133		}
2134
2135		/* LPRT/LPSV */
2136	    {
2137		int alen, af, i;
2138
2139		switch (su->su_family) {
2140		case AF_INET:
2141			a = (u_char *) &su->su_sin.sin_addr;
2142			p = (u_char *) &su->su_sin.sin_port;
2143			alen = sizeof(su->su_sin.sin_addr);
2144			af = 4;
2145			break;
2146		case AF_INET6:
2147			a = (u_char *) &su->su_sin6.sin6_addr;
2148			p = (u_char *) &su->su_sin6.sin6_port;
2149			alen = sizeof(su->su_sin6.sin6_addr);
2150			af = 6;
2151			break;
2152		default:
2153			af = 0;
2154			break;
2155		}
2156		if (af) {
2157			printf("     %s (%d,%d,", ispassive ? "LPSV" : "LPRT",
2158				af, alen);
2159			for (i = 0; i < alen; i++)
2160				printf("%d,", UC(a[i]));
2161			printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
2162		}
2163	    }
2164
2165epsvonly:;
2166		/* EPRT/EPSV */
2167	    {
2168		int af;
2169
2170		switch (su->su_family) {
2171		case AF_INET:
2172			af = 1;
2173			break;
2174		case AF_INET6:
2175			af = 2;
2176			break;
2177		default:
2178			af = 0;
2179			break;
2180		}
2181		if (af) {
2182			union sockunion tmp;
2183
2184			tmp = *su;
2185			if (tmp.su_family == AF_INET6)
2186				tmp.su_sin6.sin6_scope_id = 0;
2187			if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len,
2188					hname, sizeof(hname) - 1, NULL, 0,
2189					NI_NUMERICHOST)) {
2190				printf("     %s |%d|%s|%d|\r\n",
2191					ispassive ? "EPSV" : "EPRT",
2192					af, hname, htons(tmp.su_port));
2193			}
2194		}
2195	    }
2196#undef UC
2197	} else
2198		printf("     No data connection\r\n");
2199	reply(211, "End of status");
2200}
2201
2202void
2203fatalerror(char *s)
2204{
2205
2206	reply(451, "Error in server: %s\n", s);
2207	reply(221, "Closing connection due to server error.");
2208	dologout(0);
2209	/* NOTREACHED */
2210}
2211
2212void
2213reply(int n, const char *fmt, ...)
2214{
2215	va_list ap;
2216
2217	va_start(ap, fmt);
2218	(void)printf("%d ", n);
2219	(void)vprintf(fmt, ap);
2220	(void)printf("\r\n");
2221	(void)fflush(stdout);
2222	if (ftpdebug) {
2223		syslog(LOG_DEBUG, "<--- %d ", n);
2224		vsyslog(LOG_DEBUG, fmt, ap);
2225	}
2226}
2227
2228void
2229lreply(int n, const char *fmt, ...)
2230{
2231	va_list ap;
2232
2233	va_start(ap, fmt);
2234	(void)printf("%d- ", n);
2235	(void)vprintf(fmt, ap);
2236	(void)printf("\r\n");
2237	(void)fflush(stdout);
2238	if (ftpdebug) {
2239		syslog(LOG_DEBUG, "<--- %d- ", n);
2240		vsyslog(LOG_DEBUG, fmt, ap);
2241	}
2242}
2243
2244static void
2245ack(char *s)
2246{
2247
2248	reply(250, "%s command successful.", s);
2249}
2250
2251void
2252nack(char *s)
2253{
2254
2255	reply(502, "%s command not implemented.", s);
2256}
2257
2258/* ARGSUSED */
2259void
2260yyerror(char *s)
2261{
2262	char *cp;
2263
2264	if ((cp = strchr(cbuf,'\n')))
2265		*cp = '\0';
2266	reply(500, "'%s': command not understood.", cbuf);
2267}
2268
2269void
2270delete(char *name)
2271{
2272	struct stat st;
2273
2274	LOGCMD("delete", name);
2275	if (lstat(name, &st) < 0) {
2276		perror_reply(550, name);
2277		return;
2278	}
2279	if ((st.st_mode&S_IFMT) == S_IFDIR) {
2280		if (rmdir(name) < 0) {
2281			perror_reply(550, name);
2282			return;
2283		}
2284		goto done;
2285	}
2286	if (unlink(name) < 0) {
2287		perror_reply(550, name);
2288		return;
2289	}
2290done:
2291	ack("DELE");
2292}
2293
2294void
2295cwd(char *path)
2296{
2297
2298	if (chdir(path) < 0)
2299		perror_reply(550, path);
2300	else
2301		ack("CWD");
2302}
2303
2304void
2305makedir(char *name)
2306{
2307
2308	LOGCMD("mkdir", name);
2309	if (guest && noguestmkd)
2310		reply(550, "%s: permission denied", name);
2311	else if (mkdir(name, 0777) < 0)
2312		perror_reply(550, name);
2313	else
2314		reply(257, "MKD command successful.");
2315}
2316
2317void
2318removedir(char *name)
2319{
2320
2321	LOGCMD("rmdir", name);
2322	if (rmdir(name) < 0)
2323		perror_reply(550, name);
2324	else
2325		ack("RMD");
2326}
2327
2328void
2329pwd(void)
2330{
2331	char *s, path[MAXPATHLEN + 1];
2332
2333	if (getwd(path) == (char *)NULL)
2334		reply(550, "%s.", path);
2335	else {
2336		if ((s = doublequote(path)) == NULL)
2337			fatalerror("Ran out of memory.");
2338		reply(257, "\"%s\" is current directory.", s);
2339		free(s);
2340	}
2341}
2342
2343char *
2344renamefrom(char *name)
2345{
2346	struct stat st;
2347
2348	if (lstat(name, &st) < 0) {
2349		perror_reply(550, name);
2350		return ((char *)0);
2351	}
2352	reply(350, "File exists, ready for destination name");
2353	return (name);
2354}
2355
2356void
2357renamecmd(char *from, char *to)
2358{
2359	struct stat st;
2360
2361	LOGCMD2("rename", from, to);
2362
2363	if (guest && (stat(to, &st) == 0)) {
2364		reply(550, "%s: permission denied", to);
2365		return;
2366	}
2367
2368	if (rename(from, to) < 0)
2369		perror_reply(550, "rename");
2370	else
2371		ack("RNTO");
2372}
2373
2374static void
2375dolog(struct sockaddr *who)
2376{
2377	int error;
2378
2379	realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len);
2380
2381#ifdef SETPROCTITLE
2382#ifdef VIRTUAL_HOSTING
2383	if (thishost != firsthost)
2384		snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)",
2385			 remotehost, hostname);
2386	else
2387#endif
2388		snprintf(proctitle, sizeof(proctitle), "%s: connected",
2389			 remotehost);
2390	setproctitle("%s", proctitle);
2391#endif /* SETPROCTITLE */
2392
2393	if (logging) {
2394#ifdef VIRTUAL_HOSTING
2395		if (thishost != firsthost)
2396			syslog(LOG_INFO, "connection from %s (to %s)",
2397			       remotehost, hostname);
2398		else
2399#endif
2400		{
2401			char	who_name[MAXHOSTNAMELEN];
2402
2403			error = getnameinfo(who, who->sa_len,
2404					    who_name, sizeof(who_name) - 1,
2405					    NULL, 0, NI_NUMERICHOST);
2406			syslog(LOG_INFO, "connection from %s (%s)", remotehost,
2407			       error == 0 ? who_name : "");
2408		}
2409	}
2410}
2411
2412/*
2413 * Record logout in wtmp file
2414 * and exit with supplied status.
2415 */
2416void
2417dologout(int status)
2418{
2419	/*
2420	 * Prevent reception of SIGURG from resulting in a resumption
2421	 * back to the main program loop.
2422	 */
2423	transflag = 0;
2424
2425	if (logged_in) {
2426		(void) seteuid((uid_t)0);
2427		ftpd_logwtmp(ttyline, "", NULL);
2428	}
2429	/* beware of flushing buffers after a SIGPIPE */
2430	_exit(status);
2431}
2432
2433static void
2434sigurg(int signo)
2435{
2436
2437	recvurg = 1;
2438}
2439
2440static void
2441myoob(void)
2442{
2443	char *cp;
2444
2445	/* only process if transfer occurring */
2446	if (!transflag)
2447		return;
2448	cp = tmpline;
2449	if (getline(cp, 7, stdin) == NULL) {
2450		reply(221, "You could at least say goodbye.");
2451		dologout(0);
2452	}
2453	upper(cp);
2454	if (strcmp(cp, "ABOR\r\n") == 0) {
2455		tmpline[0] = '\0';
2456		reply(426, "Transfer aborted. Data connection closed.");
2457		reply(226, "Abort successful");
2458	}
2459	if (strcmp(cp, "STAT\r\n") == 0) {
2460		tmpline[0] = '\0';
2461		if (file_size != (off_t) -1)
2462			reply(213, "Status: %qd of %qd bytes transferred",
2463			    byte_count, file_size);
2464		else
2465			reply(213, "Status: %qd bytes transferred", byte_count);
2466	}
2467}
2468
2469/*
2470 * Note: a response of 425 is not mentioned as a possible response to
2471 *	the PASV command in RFC959. However, it has been blessed as
2472 *	a legitimate response by Jon Postel in a telephone conversation
2473 *	with Rick Adams on 25 Jan 89.
2474 */
2475void
2476passive(void)
2477{
2478	int len;
2479	char *p, *a;
2480
2481	if (pdata >= 0)		/* close old port if one set */
2482		close(pdata);
2483
2484	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
2485	if (pdata < 0) {
2486		perror_reply(425, "Can't open passive connection");
2487		return;
2488	}
2489
2490	(void) seteuid((uid_t)0);
2491
2492#ifdef IP_PORTRANGE
2493	if (ctrl_addr.su_family == AF_INET) {
2494	    int on = restricted_data_ports ? IP_PORTRANGE_HIGH
2495					   : IP_PORTRANGE_DEFAULT;
2496
2497	    if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
2498			    (char *)&on, sizeof(on)) < 0)
2499		    goto pasv_error;
2500	}
2501#endif
2502#ifdef IPV6_PORTRANGE
2503	if (ctrl_addr.su_family == AF_INET6) {
2504	    int on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
2505					   : IPV6_PORTRANGE_DEFAULT;
2506
2507	    if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
2508			    (char *)&on, sizeof(on)) < 0)
2509		    goto pasv_error;
2510	}
2511#endif
2512
2513	pasv_addr = ctrl_addr;
2514	pasv_addr.su_port = 0;
2515	if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
2516		goto pasv_error;
2517
2518	(void) seteuid((uid_t)pw->pw_uid);
2519
2520	len = sizeof(pasv_addr);
2521	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
2522		goto pasv_error;
2523	if (listen(pdata, 1) < 0)
2524		goto pasv_error;
2525	if (pasv_addr.su_family == AF_INET)
2526		a = (char *) &pasv_addr.su_sin.sin_addr;
2527	else if (pasv_addr.su_family == AF_INET6 &&
2528		 IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr))
2529		a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
2530	else
2531		goto pasv_error;
2532
2533	p = (char *) &pasv_addr.su_port;
2534
2535#define UC(b) (((int) b) & 0xff)
2536
2537	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
2538		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
2539	return;
2540
2541pasv_error:
2542	(void) seteuid((uid_t)pw->pw_uid);
2543	(void) close(pdata);
2544	pdata = -1;
2545	perror_reply(425, "Can't open passive connection");
2546	return;
2547}
2548
2549/*
2550 * Long Passive defined in RFC 1639.
2551 *     228 Entering Long Passive Mode
2552 *         (af, hal, h1, h2, h3,..., pal, p1, p2...)
2553 */
2554
2555void
2556long_passive(char *cmd, int pf)
2557{
2558	int len;
2559	char *p, *a;
2560
2561	if (pdata >= 0)		/* close old port if one set */
2562		close(pdata);
2563
2564	if (pf != PF_UNSPEC) {
2565		if (ctrl_addr.su_family != pf) {
2566			switch (ctrl_addr.su_family) {
2567			case AF_INET:
2568				pf = 1;
2569				break;
2570			case AF_INET6:
2571				pf = 2;
2572				break;
2573			default:
2574				pf = 0;
2575				break;
2576			}
2577			/*
2578			 * XXX
2579			 * only EPRT/EPSV ready clients will understand this
2580			 */
2581			if (strcmp(cmd, "EPSV") == 0 && pf) {
2582				reply(522, "Network protocol mismatch, "
2583					"use (%d)", pf);
2584			} else
2585				reply(501, "Network protocol mismatch"); /*XXX*/
2586
2587			return;
2588		}
2589	}
2590
2591	pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
2592	if (pdata < 0) {
2593		perror_reply(425, "Can't open passive connection");
2594		return;
2595	}
2596
2597	(void) seteuid((uid_t)0);
2598
2599	pasv_addr = ctrl_addr;
2600	pasv_addr.su_port = 0;
2601	len = pasv_addr.su_len;
2602
2603#ifdef IP_PORTRANGE
2604	if (ctrl_addr.su_family == AF_INET) {
2605	    int on = restricted_data_ports ? IP_PORTRANGE_HIGH
2606					   : IP_PORTRANGE_DEFAULT;
2607
2608	    if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
2609			    (char *)&on, sizeof(on)) < 0)
2610		    goto pasv_error;
2611	}
2612#endif
2613#ifdef IPV6_PORTRANGE
2614	if (ctrl_addr.su_family == AF_INET6) {
2615	    int on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
2616					   : IPV6_PORTRANGE_DEFAULT;
2617
2618	    if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
2619			    (char *)&on, sizeof(on)) < 0)
2620		    goto pasv_error;
2621	}
2622#endif
2623
2624	if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0)
2625		goto pasv_error;
2626
2627	(void) seteuid((uid_t)pw->pw_uid);
2628
2629	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
2630		goto pasv_error;
2631	if (listen(pdata, 1) < 0)
2632		goto pasv_error;
2633
2634#define UC(b) (((int) b) & 0xff)
2635
2636	if (strcmp(cmd, "LPSV") == 0) {
2637		p = (char *)&pasv_addr.su_port;
2638		switch (pasv_addr.su_family) {
2639		case AF_INET:
2640			a = (char *) &pasv_addr.su_sin.sin_addr;
2641		v4_reply:
2642			reply(228,
2643"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
2644			      4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2645			      2, UC(p[0]), UC(p[1]));
2646			return;
2647		case AF_INET6:
2648			if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) {
2649				a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
2650				goto v4_reply;
2651			}
2652			a = (char *) &pasv_addr.su_sin6.sin6_addr;
2653			reply(228,
2654"Entering Long Passive Mode "
2655"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
2656			      6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
2657			      UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
2658			      UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
2659			      UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
2660			      2, UC(p[0]), UC(p[1]));
2661			return;
2662		}
2663	} else if (strcmp(cmd, "EPSV") == 0) {
2664		switch (pasv_addr.su_family) {
2665		case AF_INET:
2666		case AF_INET6:
2667			reply(229, "Entering Extended Passive Mode (|||%d|)",
2668				ntohs(pasv_addr.su_port));
2669			return;
2670		}
2671	} else {
2672		/* more proper error code? */
2673	}
2674
2675pasv_error:
2676	(void) seteuid((uid_t)pw->pw_uid);
2677	(void) close(pdata);
2678	pdata = -1;
2679	perror_reply(425, "Can't open passive connection");
2680	return;
2681}
2682
2683/*
2684 * Generate unique name for file with basename "local".
2685 * The file named "local" is already known to exist.
2686 * Generates failure reply on error.
2687 */
2688static char *
2689gunique(char *local)
2690{
2691	static char new[MAXPATHLEN];
2692	struct stat st;
2693	int count;
2694	char *cp;
2695
2696	cp = strrchr(local, '/');
2697	if (cp)
2698		*cp = '\0';
2699	if (stat(cp ? local : ".", &st) < 0) {
2700		perror_reply(553, cp ? local : ".");
2701		return ((char *) 0);
2702	}
2703	if (cp)
2704		*cp = '/';
2705	/* -4 is for the .nn<null> we put on the end below */
2706	(void) snprintf(new, sizeof(new) - 4, "%s", local);
2707	cp = new + strlen(new);
2708	*cp++ = '.';
2709	for (count = 1; count < 100; count++) {
2710		(void)sprintf(cp, "%d", count);
2711		if (stat(new, &st) < 0)
2712			return (new);
2713	}
2714	reply(452, "Unique file name cannot be created.");
2715	return (NULL);
2716}
2717
2718/*
2719 * Format and send reply containing system error number.
2720 */
2721void
2722perror_reply(int code, char *string)
2723{
2724
2725	reply(code, "%s: %s.", string, strerror(errno));
2726}
2727
2728static char *onefile[] = {
2729	"",
2730	0
2731};
2732
2733void
2734send_file_list(char *whichf)
2735{
2736	struct stat st;
2737	DIR *dirp = NULL;
2738	struct dirent *dir;
2739	FILE *dout = NULL;
2740	char **dirlist, *dirname;
2741	int simple = 0;
2742	int freeglob = 0;
2743	glob_t gl;
2744
2745	if (strpbrk(whichf, "~{[*?") != NULL) {
2746		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
2747
2748		memset(&gl, 0, sizeof(gl));
2749		gl.gl_matchc = MAXGLOBARGS;
2750		flags |= GLOB_LIMIT;
2751		freeglob = 1;
2752		if (glob(whichf, flags, 0, &gl)) {
2753			reply(550, "not found");
2754			goto out;
2755		} else if (gl.gl_pathc == 0) {
2756			errno = ENOENT;
2757			perror_reply(550, whichf);
2758			goto out;
2759		}
2760		dirlist = gl.gl_pathv;
2761	} else {
2762		onefile[0] = whichf;
2763		dirlist = onefile;
2764		simple = 1;
2765	}
2766
2767	while ((dirname = *dirlist++)) {
2768		if (stat(dirname, &st) < 0) {
2769			/*
2770			 * If user typed "ls -l", etc, and the client
2771			 * used NLST, do what the user meant.
2772			 */
2773			if (dirname[0] == '-' && *dirlist == NULL &&
2774			    transflag == 0) {
2775				retrieve(_PATH_LS " %s", dirname);
2776				goto out;
2777			}
2778			perror_reply(550, whichf);
2779			if (dout != NULL) {
2780				(void) fclose(dout);
2781				transflag = 0;
2782				data = -1;
2783				pdata = -1;
2784			}
2785			goto out;
2786		}
2787
2788		if (S_ISREG(st.st_mode)) {
2789			if (dout == NULL) {
2790				dout = dataconn("file list", (off_t)-1, "w");
2791				if (dout == NULL)
2792					goto out;
2793				transflag++;
2794			}
2795			fprintf(dout, "%s%s\n", dirname,
2796				type == TYPE_A ? "\r" : "");
2797			byte_count += strlen(dirname) + 1;
2798			continue;
2799		} else if (!S_ISDIR(st.st_mode))
2800			continue;
2801
2802		if ((dirp = opendir(dirname)) == NULL)
2803			continue;
2804
2805		while ((dir = readdir(dirp)) != NULL) {
2806			char nbuf[MAXPATHLEN];
2807
2808			if (recvurg) {
2809				myoob();
2810				recvurg = 0;
2811				transflag = 0;
2812				goto out;
2813			}
2814
2815			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
2816				continue;
2817			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
2818			    dir->d_namlen == 2)
2819				continue;
2820
2821			snprintf(nbuf, sizeof(nbuf),
2822				"%s/%s", dirname, dir->d_name);
2823
2824			/*
2825			 * We have to do a stat to insure it's
2826			 * not a directory or special file.
2827			 */
2828			if (simple || (stat(nbuf, &st) == 0 &&
2829			    S_ISREG(st.st_mode))) {
2830				if (dout == NULL) {
2831					dout = dataconn("file list", (off_t)-1,
2832						"w");
2833					if (dout == NULL)
2834						goto out;
2835					transflag++;
2836				}
2837				if (nbuf[0] == '.' && nbuf[1] == '/')
2838					fprintf(dout, "%s%s\n", &nbuf[2],
2839						type == TYPE_A ? "\r" : "");
2840				else
2841					fprintf(dout, "%s%s\n", nbuf,
2842						type == TYPE_A ? "\r" : "");
2843				byte_count += strlen(nbuf) + 1;
2844			}
2845		}
2846		(void) closedir(dirp);
2847	}
2848
2849	if (dout == NULL)
2850		reply(550, "No files found.");
2851	else if (ferror(dout) != 0)
2852		perror_reply(550, "Data connection");
2853	else
2854		reply(226, "Transfer complete.");
2855
2856	transflag = 0;
2857	if (dout != NULL)
2858		(void) fclose(dout);
2859	data = -1;
2860	pdata = -1;
2861out:
2862	if (freeglob) {
2863		freeglob = 0;
2864		globfree(&gl);
2865	}
2866}
2867
2868void
2869reapchild(int signo)
2870{
2871	while (wait3(NULL, WNOHANG, NULL) > 0);
2872}
2873
2874#ifdef OLD_SETPROCTITLE
2875/*
2876 * Clobber argv so ps will show what we're doing.  (Stolen from sendmail.)
2877 * Warning, since this is usually started from inetd.conf, it often doesn't
2878 * have much of an environment or arglist to overwrite.
2879 */
2880void
2881setproctitle(const char *fmt, ...)
2882{
2883	int i;
2884	va_list ap;
2885	char *p, *bp, ch;
2886	char buf[LINE_MAX];
2887
2888	va_start(ap, fmt);
2889	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
2890
2891	/* make ps print our process name */
2892	p = Argv[0];
2893	*p++ = '-';
2894
2895	i = strlen(buf);
2896	if (i > LastArgv - p - 2) {
2897		i = LastArgv - p - 2;
2898		buf[i] = '\0';
2899	}
2900	bp = buf;
2901	while (ch = *bp++)
2902		if (ch != '\n' && ch != '\r')
2903			*p++ = ch;
2904	while (p < LastArgv)
2905		*p++ = ' ';
2906}
2907#endif /* OLD_SETPROCTITLE */
2908
2909static void
2910logxfer(char *name, off_t size, time_t start)
2911{
2912	char buf[1024];
2913	char path[MAXPATHLEN + 1];
2914	time_t now;
2915
2916	if (statfd >= 0 && getwd(path) != NULL) {
2917		time(&now);
2918		snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%qd!%ld\n",
2919			ctime(&now)+4, ident, remotehost,
2920			path, name, (long long)size,
2921			(long)(now - start + (now == start)));
2922		write(statfd, buf, strlen(buf));
2923	}
2924}
2925
2926static char *
2927doublequote(char *s)
2928{
2929	int n;
2930	char *p, *s2;
2931
2932	for (p = s, n = 0; *p; p++)
2933		if (*p == '"')
2934			n++;
2935
2936	if ((s2 = malloc(p - s + n + 1)) == NULL)
2937		return (NULL);
2938
2939	for (p = s2; *s; s++, p++) {
2940		if ((*p = *s) == '"')
2941			*(++p) = '"';
2942	}
2943	*p = '\0';
2944
2945	return (s2);
2946}
2947