xref: /netbsd-src/libexec/ftpd/ftpd.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: ftpd.c,v 1.28 1997/06/22 22:33:13 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT(
39 "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\
40 	The Regents of the University of California.  All rights reserved.\n");
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)ftpd.c	8.5 (Berkeley) 4/28/95";
46 #else
47 __RCSID("$NetBSD: ftpd.c,v 1.28 1997/06/22 22:33:13 christos Exp $");
48 #endif
49 #endif /* not lint */
50 
51 /*
52  * FTP server.
53  */
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/wait.h>
59 
60 #include <netinet/in.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63 
64 #define	FTP_NAMES
65 #include <arpa/ftp.h>
66 #include <arpa/inet.h>
67 #include <arpa/telnet.h>
68 
69 #include <ctype.h>
70 #include <dirent.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <fnmatch.h>
75 #include <glob.h>
76 #include <limits.h>
77 #include <netdb.h>
78 #include <pwd.h>
79 #include <setjmp.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <syslog.h>
85 #include <time.h>
86 #include <unistd.h>
87 #ifdef SKEY
88 #include <skey.h>
89 #endif
90 
91 #include "extern.h"
92 #include "pathnames.h"
93 
94 #if __STDC__
95 #include <stdarg.h>
96 #else
97 #include <varargs.h>
98 #endif
99 
100 static char version[] = "Version 7.00";
101 
102 extern	off_t restart_point;
103 extern	char cbuf[];
104 extern  int yyparse __P((void));
105 
106 struct	sockaddr_in ctrl_addr;
107 struct	sockaddr_in data_source;
108 struct	sockaddr_in data_dest;
109 struct	sockaddr_in his_addr;
110 struct	sockaddr_in pasv_addr;
111 
112 int	data;
113 jmp_buf	errcatch, urgcatch;
114 int	logged_in;
115 struct	passwd *pw;
116 int	debug;
117 int	logging;
118 int	guest;
119 int	dochroot;
120 int	type;
121 int	form;
122 int	stru;			/* avoid C keyword */
123 int	mode;
124 int	usedefault = 1;		/* for data transfers */
125 int	pdata = -1;		/* for passive mode */
126 sig_atomic_t transflag;
127 off_t	file_size;
128 off_t	byte_count;
129 char	tmpline[7];
130 char	hostname[MAXHOSTNAMELEN];
131 char	remotehost[MAXHOSTNAMELEN];
132 static char ttyline[20];
133 char	*tty = ttyline;		/* for klogin */
134 static char *anondir = NULL;
135 
136 extern struct ftpclass curclass;
137 
138 #if defined(KERBEROS)
139 int	notickets = 1;
140 char	*krbtkfile_env = NULL;
141 #endif
142 
143 /*
144  * Timeout intervals for retrying connections
145  * to hosts that don't accept PORT cmds.  This
146  * is a kludge, but given the problems with TCP...
147  */
148 #define	SWAITMAX	90	/* wait at most 90 seconds */
149 #define	SWAITINT	5	/* interval between retries */
150 
151 int	swaitmax = SWAITMAX;
152 int	swaitint = SWAITINT;
153 
154 #ifdef HASSETPROCTITLE
155 char	proctitle[BUFSIZ];	/* initial part of title */
156 #endif /* HASSETPROCTITLE */
157 
158 #define LOGCMD(cmd, file) \
159 	if (logging > 1) \
160 	    syslog(LOG_INFO,"%s %s%s", cmd, \
161 		*(file) == '/' ? "" : curdir(), file);
162 #define LOGCMD2(cmd, file1, file2) \
163 	 if (logging > 1) \
164 	    syslog(LOG_INFO,"%s %s%s %s%s", cmd, \
165 		*(file1) == '/' ? "" : curdir(), file1, \
166 		*(file2) == '/' ? "" : curdir(), file2);
167 #define LOGBYTES(cmd, file, cnt) \
168 	if (logging > 1) { \
169 		if (cnt == (off_t)-1) \
170 		    syslog(LOG_INFO,"%s %s%s", cmd, \
171 			*(file) == '/' ? "" : curdir(), file); \
172 		else \
173 		    syslog(LOG_INFO, "%s %s%s = %qd bytes", \
174 			cmd, (*(file) == '/') ? "" : curdir(), file, \
175 			(long long)cnt); \
176 	}
177 
178 static void	 ack __P((char *));
179 static void	 myoob __P((int));
180 static int	 checkuser __P((char *, char *));
181 static int	 checkaccess __P((char *));
182 static FILE	*dataconn __P((char *, off_t, char *));
183 static void	 dolog __P((struct sockaddr_in *));
184 static char	*curdir __P((void));
185 static void	 end_login __P((void));
186 static FILE	*getdatasock __P((char *));
187 static char	*gunique __P((char *));
188 static void	 lostconn __P((int));
189 static int	 receive_data __P((FILE *, FILE *));
190 static void	 send_data __P((FILE *, FILE *, off_t));
191 static struct passwd *
192 		 sgetpwnam __P((char *));
193 static char	*sgetsave __P((char *));
194 
195 int main __P((int, char *[], char **));
196 
197 #if defined (KERBEROS)
198 int  klogin __P((struct passwd *, char *, char *, char *));
199 #endif
200 
201 static char *
202 curdir()
203 {
204 	static char path[MAXPATHLEN+1+1];	/* path + '/' + '\0' */
205 
206 	if (getcwd(path, sizeof(path)-2) == NULL)
207 		return ("");
208 	if (path[1] != '\0')		/* special case for root dir. */
209 		strcat(path, "/");
210 	/* For guest account, skip / since it's chrooted */
211 	return (guest ? path+1 : path);
212 }
213 
214 int
215 main(argc, argv, envp)
216 	int argc;
217 	char *argv[];
218 	char **envp;
219 {
220 	int addrlen, ch, on = 1, tos;
221 	char *cp, line[LINE_MAX];
222 	FILE *fd;
223 
224 	/*
225 	 * LOG_NDELAY sets up the logging connection immediately,
226 	 * necessary for anonymous ftp's that chroot and can't do it later.
227 	 */
228 	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
229 	addrlen = sizeof(his_addr);
230 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
231 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
232 		exit(1);
233 	}
234 	addrlen = sizeof(ctrl_addr);
235 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
236 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
237 		exit(1);
238 	}
239 #ifdef IP_TOS
240 	tos = IPTOS_LOWDELAY;
241 	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
242 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
243 #endif
244 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
245 	debug = 0;
246 
247 	/* set this here so klogin can use it... */
248 	(void)sprintf(ttyline, "ftp%d", getpid());
249 
250 	while ((ch = getopt(argc, argv, "a:dlt:T:u:v")) != EOF) {
251 		switch (ch) {
252 		case 'a':
253 			anondir = optarg;
254 			break;
255 
256 		case 'd':
257 		case 'v':		/* deprecated */
258 			debug = 1;
259 			break;
260 
261 		case 'l':
262 			logging++;	/* > 1 == extra logging */
263 			break;
264 
265 		case 't':
266 		case 'T':
267 		case 'u':
268 			warnx("-%c has be deprecated in favour of ftpd.conf",
269 			    ch);
270 			break;
271 
272 		default:
273 			warnx("unknown flag -%c ignored", optopt);
274 			break;
275 		}
276 	}
277 	(void) freopen(_PATH_DEVNULL, "w", stderr);
278 	(void) signal(SIGPIPE, lostconn);
279 	(void) signal(SIGCHLD, SIG_IGN);
280 	if ((long)signal(SIGURG, myoob) < 0)
281 		syslog(LOG_ERR, "signal: %m");
282 
283 	/* Try to handle urgent data inline */
284 #ifdef SO_OOBINLINE
285 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
286 		syslog(LOG_ERR, "setsockopt: %m");
287 #endif
288 
289 #ifdef	F_SETOWN
290 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
291 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
292 #endif
293 	dolog(&his_addr);
294 	/*
295 	 * Set up default state
296 	 */
297 	data = -1;
298 	type = TYPE_A;
299 	form = FORM_N;
300 	stru = STRU_F;
301 	mode = MODE_S;
302 	tmpline[0] = '\0';
303 
304 	/* If logins are disabled, print out the message. */
305 	if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
306 		while (fgets(line, sizeof(line), fd) != NULL) {
307 			if ((cp = strchr(line, '\n')) != NULL)
308 				*cp = '\0';
309 			lreply(530, "%s", line);
310 		}
311 		(void) fflush(stdout);
312 		(void) fclose(fd);
313 		reply(530, "System not available.");
314 		exit(0);
315 	}
316 	if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
317 		while (fgets(line, sizeof(line), fd) != NULL) {
318 			if ((cp = strchr(line, '\n')) != NULL)
319 				*cp = '\0';
320 			lreply(220, "%s", line);
321 		}
322 		(void) fflush(stdout);
323 		(void) fclose(fd);
324 		/* reply(220,) must follow */
325 	}
326 	(void) gethostname(hostname, sizeof(hostname));
327 	reply(220, "%s FTP server (%s) ready.", hostname, version);
328 	(void) setjmp(errcatch);
329 	for (;;)
330 		(void) yyparse();
331 	/* NOTREACHED */
332 }
333 
334 static void
335 lostconn(signo)
336 	int signo;
337 {
338 
339 	if (debug)
340 		syslog(LOG_DEBUG, "lost connection");
341 	dologout(-1);
342 }
343 
344 /*
345  * Helper function for sgetpwnam().
346  */
347 static char *
348 sgetsave(s)
349 	char *s;
350 {
351 	char *new = malloc((unsigned) strlen(s) + 1);
352 
353 	if (new == NULL) {
354 		perror_reply(421, "Local resource failure: malloc");
355 		dologout(1);
356 		/* NOTREACHED */
357 	}
358 	(void) strcpy(new, s);
359 	return (new);
360 }
361 
362 /*
363  * Save the result of a getpwnam.  Used for USER command, since
364  * the data returned must not be clobbered by any other command
365  * (e.g., globbing).
366  */
367 static struct passwd *
368 sgetpwnam(name)
369 	char *name;
370 {
371 	static struct passwd save;
372 	struct passwd *p;
373 
374 	if ((p = getpwnam(name)) == NULL)
375 		return (p);
376 	if (save.pw_name) {
377 		free(save.pw_name);
378 		free(save.pw_passwd);
379 		free(save.pw_gecos);
380 		free(save.pw_dir);
381 		free(save.pw_shell);
382 	}
383 	save = *p;
384 	save.pw_name = sgetsave(p->pw_name);
385 	save.pw_passwd = sgetsave(p->pw_passwd);
386 	save.pw_gecos = sgetsave(p->pw_gecos);
387 	save.pw_dir = sgetsave(p->pw_dir);
388 	save.pw_shell = sgetsave(p->pw_shell);
389 	return (&save);
390 }
391 
392 static int login_attempts;	/* number of failed login attempts */
393 static int askpasswd;		/* had user command, ask for passwd */
394 static char curname[10];	/* current USER name */
395 
396 /*
397  * USER command.
398  * Sets global passwd pointer pw if named account exists and is acceptable;
399  * sets askpasswd if a PASS command is expected.  If logged in previously,
400  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
401  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
403  * requesting login privileges.  Disallow anyone who does not have a standard
404  * shell as returned by getusershell().  Disallow anyone mentioned in the file
405  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
406  */
407 void
408 user(name)
409 	char *name;
410 {
411 	if (logged_in) {
412 		if (guest) {
413 			reply(530, "Can't change user from guest login.");
414 			return;
415 		} else if (dochroot) {
416 			reply(530, "Can't change user from chroot user.");
417 			return;
418 		}
419 		end_login();
420 	}
421 
422 	guest = 0;
423 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
424 		if (checkaccess("ftp") || checkaccess("anonymous"))
425 			reply(530, "User %s access denied.", name);
426 		else if ((pw = sgetpwnam("ftp")) != NULL) {
427 			guest = 1;
428 			askpasswd = 1;
429 			reply(331,
430 			    "Guest login ok, type your name as password.");
431 		} else
432 			reply(530, "User %s unknown.", name);
433 		if (!askpasswd && logging)
434 			syslog(LOG_NOTICE,
435 			    "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
436 		return;
437 	}
438 	pw = sgetpwnam(name);
439 	if (logging)
440 		strncpy(curname, name, sizeof(curname)-1);
441 #ifdef SKEY
442 	if (!skey_haskey(name)) {
443 		char *myskey, *skey_keyinfo __P((char *name));
444 
445 		myskey = skey_keyinfo(name);
446 		reply(331, "Password [%s] for %s required.",
447 		    myskey ? myskey : "error getting challenge", name);
448 	} else
449 #endif
450 		reply(331, "Password required for %s.", name);
451 
452 	askpasswd = 1;
453 	/*
454 	 * Delay before reading passwd after first failed
455 	 * attempt to slow down passwd-guessing programs.
456 	 */
457 	if (login_attempts)
458 		sleep((unsigned) login_attempts);
459 }
460 
461 /*
462  * Check if a user is in the file "fname"
463  */
464 static int
465 checkuser(fname, name)
466 	char *fname;
467 	char *name;
468 {
469 	FILE *fd;
470 	int found = 0;
471 	char *p, line[BUFSIZ];
472 
473 	if ((fd = fopen(fname, "r")) != NULL) {
474 		while (fgets(line, sizeof(line), fd) != NULL)
475 			if ((p = strchr(line, '\n')) != NULL) {
476 				*p = '\0';
477 				if (line[0] == '#')
478 					continue;
479 				if (strcmp(line, name) == 0) {
480 					found = 1;
481 					break;
482 				}
483 			}
484 		(void) fclose(fd);
485 	}
486 	return (found);
487 }
488 
489 /*
490  * Determine whether a user has access, based on information in
491  * _PATH_FTPUSERS. Each line is a shell-style glob followed by
492  * `allow' or `deny' (with deny being the default if anything but
493  * `allow', or nothing at all, is specified).
494  *
495  * Each glob is matched against the username in turn, and the first
496  * match found is used. If no match is found, access is allowed.
497  *
498  * Any line starting with `#' is considered a comment and ignored.
499  *
500  * This is probably not the best way to do this, but it preserves
501  * the old semantics where if a user was listed in the file he was
502  * denied, otherwise he was allowed.
503  *
504  * There is one change in the semantics, however; ftpd will now `fail
505  * safe' and deny all access if there's no /etc/ftpusers file.
506  *
507  * Return 1 if the user is denied, or 0 if he is allowed.
508  */
509 static int
510 checkaccess(name)
511 	char *name;
512 {
513 #define ALLOWED		0
514 #define	NOT_ALLOWED	1
515 	FILE *fd;
516 	int retval = ALLOWED;
517 	char *glob, *perm, line[BUFSIZ];
518 
519 	if ((fd = fopen(_PATH_FTPUSERS, "r")) == NULL)
520 		return NOT_ALLOWED;
521 
522 	while (fgets(line, sizeof(line), fd) != NULL)  {
523 		glob = strtok(line, " \t\n");
524 		if (glob[0] == '#')
525 			continue;
526 		perm = strtok(NULL, " \t\n");
527 		if (fnmatch(glob, name, 0) == 0)  {
528 			if (perm != NULL && strcmp(perm, "allow") == 0)
529 				retval = ALLOWED;
530 			else
531 				retval = NOT_ALLOWED;
532 			break;
533 		}
534 	}
535 	(void) fclose(fd);
536 	return (retval);
537 
538 }
539 #undef	ALLOWED
540 #undef	NOT_ALLOWED
541 
542 /*
543  * Terminate login as previous user, if any, resetting state;
544  * used when USER command is given or login fails.
545  */
546 static void
547 end_login()
548 {
549 
550 	(void) seteuid((uid_t)0);
551 	if (logged_in)
552 		logwtmp(ttyline, "", "");
553 	pw = NULL;
554 	logged_in = 0;
555 	guest = 0;
556 	dochroot = 0;
557 }
558 
559 void
560 pass(passwd)
561 	char *passwd;
562 {
563 	int rval;
564 	FILE *fd;
565 	char *cp, *shell;
566 
567 	if (logged_in || askpasswd == 0) {
568 		reply(503, "Login with USER first.");
569 		return;
570 	}
571 	askpasswd = 0;
572 	if (!guest) {		/* "ftp" is only account allowed no password */
573 		if (pw == NULL) {
574 			rval = 1;	/* failure below */
575 			goto skip;
576 		}
577 #if defined(KERBEROS)
578 		rval = klogin(pw, "", hostname, passwd);
579 		if (rval == 0)
580 			goto skip;
581 #endif
582 #ifdef SKEY
583 		if (skey_haskey(pw->pw_name) == 0 &&
584 		   (skey_passcheck(pw->pw_name, passwd) != -1)) {
585 			rval = 0;
586 			goto skip;
587 		}
588 #endif
589 		/* the strcmp does not catch null passwords! */
590 		if (pw == NULL || *pw->pw_passwd == '\0' ||
591 		    strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) {
592 			rval = 1;	 /* failure */
593 			goto skip;
594 		}
595 		rval = 0;
596 
597 skip:
598 		/*
599 		 * If rval == 1, the user failed the authentication check
600 		 * above.  If rval == 0, either Kerberos or local authentication
601 		 * succeeded.
602 		 */
603 		if (rval) {
604 			reply(530, "Login incorrect.");
605 			if (logging) {
606 				syslog(LOG_NOTICE,
607 				    "FTP LOGIN FAILED FROM %s", remotehost);
608 				syslog(LOG_AUTHPRIV | LOG_NOTICE,
609 				    "FTP LOGIN FAILED FROM %s, %s",
610 				    remotehost, curname);
611 			}
612 			pw = NULL;
613 			if (login_attempts++ >= 5) {
614 				syslog(LOG_NOTICE,
615 				    "repeated login failures from %s",
616 				    remotehost);
617 				exit(0);
618 			}
619 			return;
620 		}
621 	}
622 
623 	/* password was ok; see if anything else prevents login */
624 	if (checkaccess(pw->pw_name))  {
625 		reply(530, "User %s may not use FTP.", pw->pw_name);
626 		if (logging)
627 			syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
628 			    remotehost, pw->pw_name);
629 		pw = (struct passwd *) NULL;
630 		return;
631 	}
632 	/* check for valid shell, if not guest user */
633 	if ((shell = pw->pw_shell) == NULL || *shell == 0)
634 		shell = _PATH_BSHELL;
635 	while ((cp = getusershell()) != NULL)
636 		if (strcmp(cp, shell) == 0)
637 			break;
638 	endusershell();
639 	if (cp == NULL && guest == 0) {
640 		reply(530, "User %s may not use FTP.", pw->pw_name);
641 		if (logging)
642 			syslog(LOG_NOTICE,
643 			    "FTP LOGIN REFUSED FROM %s, %s",
644 			    remotehost, pw->pw_name);
645 		pw = (struct passwd *) NULL;
646 		return;
647 	}
648 
649 	login_attempts = 0;		/* this time successful */
650 	if (setegid((gid_t)pw->pw_gid) < 0) {
651 		reply(550, "Can't set gid.");
652 		return;
653 	}
654 	(void) initgroups(pw->pw_name, pw->pw_gid);
655 
656 	/* open wtmp before chroot */
657 	logwtmp(ttyline, pw->pw_name, remotehost);
658 	logged_in = 1;
659 
660 	dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
661 
662 	/* parse ftpd.conf, setting up various parameters */
663 	if (guest)
664 		parse_conf(CLASS_GUEST);
665 	else if (dochroot)
666 		parse_conf(CLASS_CHROOT);
667 	else
668 		parse_conf(CLASS_REAL);
669 
670 	if (guest) {
671 		/*
672 		 * We MUST do a chdir() after the chroot. Otherwise
673 		 * the old current directory will be accessible as "."
674 		 * outside the new root!
675 		 */
676 		if (chroot(anondir ? anondir : pw->pw_dir) < 0 ||
677 		    chdir("/") < 0) {
678 			reply(550, "Can't set guest privileges.");
679 			goto bad;
680 		}
681 	} else if (dochroot) {
682 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
683 			reply(550, "Can't change root.");
684 			goto bad;
685 		}
686 	} else if (chdir(pw->pw_dir) < 0) {
687 		if (chdir("/") < 0) {
688 			reply(530, "User %s: can't change directory to %s.",
689 			    pw->pw_name, pw->pw_dir);
690 			goto bad;
691 		} else
692 			lreply(230, "No directory! Logging in with home=/");
693 	}
694 	if (seteuid((uid_t)pw->pw_uid) < 0) {
695 		reply(550, "Can't set uid.");
696 		goto bad;
697 	}
698 	/*
699 	 * Display a login message, if it exists.
700 	 * N.B. reply(230,) must follow the message.
701 	 */
702 	if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
703 		char *cp, line[LINE_MAX];
704 
705 		while (fgets(line, sizeof(line), fd) != NULL) {
706 			if ((cp = strchr(line, '\n')) != NULL)
707 				*cp = '\0';
708 			lreply(230, "%s", line);
709 		}
710 		(void) fflush(stdout);
711 		(void) fclose(fd);
712 	}
713 	show_chdir_messages(230);
714 	if (guest) {
715 		reply(230, "Guest login ok, access restrictions apply.");
716 #ifdef HASSETPROCTITLE
717 		snprintf(proctitle, sizeof(proctitle),
718 		    "%s: anonymous/%.*s", remotehost,
719 		    (int) (sizeof(proctitle) - sizeof(remotehost) -
720 		    sizeof(": anonymous/")), passwd);
721 		setproctitle(proctitle);
722 #endif /* HASSETPROCTITLE */
723 		if (logging)
724 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
725 			    remotehost, passwd);
726 	} else {
727 		reply(230, "User %s logged in.", pw->pw_name);
728 #ifdef HASSETPROCTITLE
729 		snprintf(proctitle, sizeof(proctitle),
730 		    "%s: %s", remotehost, pw->pw_name);
731 		setproctitle(proctitle);
732 #endif /* HASSETPROCTITLE */
733 		if (logging)
734 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
735 			    remotehost, pw->pw_name);
736 	}
737 	(void) umask(curclass.umask);
738 	return;
739 bad:
740 	/* Forget all about it... */
741 	end_login();
742 }
743 
744 void
745 retrieve(cmd, name)
746 	char *cmd, *name;
747 {
748 	FILE *fin = NULL, *dout;
749 	struct stat st;
750 	int (*closefunc) __P((FILE *)) = NULL;
751 	int log;
752 
753 	log = (cmd == 0);
754 	if (cmd == 0) {
755 		fin = fopen(name, "r"), closefunc = fclose;
756 		if (fin == NULL)
757 			cmd = do_conversion(name);
758 	}
759 	if (cmd) {
760 		char line[BUFSIZ];
761 
762 		(void) sprintf(line, cmd, name), name = line;
763 		fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
764 		st.st_size = -1;
765 		st.st_blksize = BUFSIZ;
766 	}
767 	if (fin == NULL) {
768 		if (errno != 0) {
769 			perror_reply(550, name);
770 			if (log) {
771 				LOGCMD("get", name);
772 			}
773 		}
774 		return;
775 	}
776 	byte_count = -1;
777 	if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) {
778 		reply(550, "%s: not a plain file.", name);
779 		goto done;
780 	}
781 	if (restart_point) {
782 		if (type == TYPE_A) {
783 			off_t i, n;
784 			int c;
785 
786 			n = restart_point;
787 			i = 0;
788 			while (i++ < n) {
789 				if ((c=getc(fin)) == EOF) {
790 					perror_reply(550, name);
791 					goto done;
792 				}
793 				if (c == '\n')
794 					i++;
795 			}
796 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
797 			perror_reply(550, name);
798 			goto done;
799 		}
800 	}
801 	dout = dataconn(name, st.st_size, "w");
802 	if (dout == NULL)
803 		goto done;
804 	send_data(fin, dout, st.st_blksize);
805 	(void) fclose(dout);
806 	data = -1;
807 	pdata = -1;
808 done:
809 	if (log)
810 		LOGBYTES("get", name, byte_count);
811 	(*closefunc)(fin);
812 }
813 
814 void
815 store(name, mode, unique)
816 	char *name, *mode;
817 	int unique;
818 {
819 	FILE *fout, *din;
820 	struct stat st;
821 	int (*closefunc) __P((FILE *));
822 
823 	if (unique && stat(name, &st) == 0 &&
824 	    (name = gunique(name)) == NULL) {
825 		LOGCMD(*mode == 'w' ? "put" : "append", name);
826 		return;
827 	}
828 
829 	if (restart_point)
830 		mode = "r+";
831 	fout = fopen(name, mode);
832 	closefunc = fclose;
833 	if (fout == NULL) {
834 		perror_reply(553, name);
835 		LOGCMD(*mode == 'w' ? "put" : "append", name);
836 		return;
837 	}
838 	byte_count = -1;
839 	if (restart_point) {
840 		if (type == TYPE_A) {
841 			off_t i, n;
842 			int c;
843 
844 			n = restart_point;
845 			i = 0;
846 			while (i++ < n) {
847 				if ((c=getc(fout)) == EOF) {
848 					perror_reply(550, name);
849 					goto done;
850 				}
851 				if (c == '\n')
852 					i++;
853 			}
854 			/*
855 			 * We must do this seek to "current" position
856 			 * because we are changing from reading to
857 			 * writing.
858 			 */
859 			if (fseek(fout, 0L, SEEK_CUR) < 0) {
860 				perror_reply(550, name);
861 				goto done;
862 			}
863 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
864 			perror_reply(550, name);
865 			goto done;
866 		}
867 	}
868 	din = dataconn(name, (off_t)-1, "r");
869 	if (din == NULL)
870 		goto done;
871 	if (receive_data(din, fout) == 0) {
872 		if (unique)
873 			reply(226, "Transfer complete (unique file name:%s).",
874 			    name);
875 		else
876 			reply(226, "Transfer complete.");
877 	}
878 	(void) fclose(din);
879 	data = -1;
880 	pdata = -1;
881 done:
882 	LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count);
883 	(*closefunc)(fout);
884 }
885 
886 static FILE *
887 getdatasock(mode)
888 	char *mode;
889 {
890 	int on = 1, s, t, tries;
891 
892 	if (data >= 0)
893 		return (fdopen(data, mode));
894 	(void) seteuid((uid_t)0);
895 	s = socket(AF_INET, SOCK_STREAM, 0);
896 	if (s < 0)
897 		goto bad;
898 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
899 	    (char *) &on, sizeof(on)) < 0)
900 		goto bad;
901 	/* anchor socket to avoid multi-homing problems */
902 	data_source.sin_len = sizeof(struct sockaddr_in);
903 	data_source.sin_family = AF_INET;
904 	data_source.sin_addr = ctrl_addr.sin_addr;
905 	for (tries = 1; ; tries++) {
906 		if (bind(s, (struct sockaddr *)&data_source,
907 		    sizeof(data_source)) >= 0)
908 			break;
909 		if (errno != EADDRINUSE || tries > 10)
910 			goto bad;
911 		sleep(tries);
912 	}
913 	(void) seteuid((uid_t)pw->pw_uid);
914 #ifdef IP_TOS
915 	on = IPTOS_THROUGHPUT;
916 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
917 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
918 #endif
919 	return (fdopen(s, mode));
920 bad:
921 	/* Return the real value of errno (close may change it) */
922 	t = errno;
923 	(void) seteuid((uid_t)pw->pw_uid);
924 	(void) close(s);
925 	errno = t;
926 	return (NULL);
927 }
928 
929 static FILE *
930 dataconn(name, size, mode)
931 	char *name;
932 	off_t size;
933 	char *mode;
934 {
935 	char sizebuf[32];
936 	FILE *file;
937 	int retry = 0, tos;
938 
939 	file_size = size;
940 	byte_count = 0;
941 	if (size != (off_t) -1)
942 		(void) sprintf(sizebuf, " (%qd bytes)", (long long)size);
943 	else
944 		(void) strcpy(sizebuf, "");
945 	if (pdata >= 0) {
946 		struct sockaddr_in from;
947 		int s, fromlen = sizeof(from);
948 
949 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
950 		if (s < 0) {
951 			reply(425, "Can't open data connection.");
952 			(void) close(pdata);
953 			pdata = -1;
954 			return (NULL);
955 		}
956 		(void) close(pdata);
957 		pdata = s;
958 #ifdef IP_TOS
959 		tos = IPTOS_THROUGHPUT;
960 		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
961 		    sizeof(int));
962 #endif
963 		reply(150, "Opening %s mode data connection for '%s'%s.",
964 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
965 		return (fdopen(pdata, mode));
966 	}
967 	if (data >= 0) {
968 		reply(125, "Using existing data connection for '%s'%s.",
969 		    name, sizebuf);
970 		usedefault = 1;
971 		return (fdopen(data, mode));
972 	}
973 	if (usedefault)
974 		data_dest = his_addr;
975 	usedefault = 1;
976 	file = getdatasock(mode);
977 	if (file == NULL) {
978 		reply(425, "Can't create data socket (%s,%d): %s.",
979 		    inet_ntoa(data_source.sin_addr),
980 		    ntohs(data_source.sin_port), strerror(errno));
981 		return (NULL);
982 	}
983 	data = fileno(file);
984 	while (connect(data, (struct sockaddr *)&data_dest,
985 	    sizeof(data_dest)) < 0) {
986 		if (errno == EADDRINUSE && retry < swaitmax) {
987 			sleep((unsigned) swaitint);
988 			retry += swaitint;
989 			continue;
990 		}
991 		perror_reply(425, "Can't build data connection");
992 		(void) fclose(file);
993 		data = -1;
994 		return (NULL);
995 	}
996 	reply(150, "Opening %s mode data connection for '%s'%s.",
997 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
998 	return (file);
999 }
1000 
1001 /*
1002  * Tranfer the contents of "instr" to "outstr" peer using the appropriate
1003  * encapsulation of the data subject * to Mode, Structure, and Type.
1004  *
1005  * NB: Form isn't handled.
1006  */
1007 static void
1008 send_data(instr, outstr, blksize)
1009 	FILE *instr, *outstr;
1010 	off_t blksize;
1011 {
1012 	int	 c, cnt, filefd, netfd;
1013 	char	*buf;
1014 
1015 	transflag++;
1016 	if (setjmp(urgcatch)) {
1017 		transflag = 0;
1018 		return;
1019 	}
1020 
1021 	switch (type) {
1022 
1023 	case TYPE_A:
1024 		while ((c = getc(instr)) != EOF) {
1025 			byte_count++;
1026 			if (c == '\n') {
1027 				if (ferror(outstr))
1028 					goto data_err;
1029 				(void) putc('\r', outstr);
1030 			}
1031 			(void) putc(c, outstr);
1032 		}
1033 		fflush(outstr);
1034 		transflag = 0;
1035 		if (ferror(instr))
1036 			goto file_err;
1037 		if (ferror(outstr))
1038 			goto data_err;
1039 		reply(226, "Transfer complete.");
1040 		return;
1041 
1042 	case TYPE_I:
1043 	case TYPE_L:
1044 		if ((buf = malloc((u_int)blksize)) == NULL) {
1045 			transflag = 0;
1046 			perror_reply(451, "Local resource failure: malloc");
1047 			return;
1048 		}
1049 		netfd = fileno(outstr);
1050 		filefd = fileno(instr);
1051 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
1052 		    write(netfd, buf, cnt) == cnt)
1053 			byte_count += cnt;
1054 		transflag = 0;
1055 		(void)free(buf);
1056 		if (cnt != 0) {
1057 			if (cnt < 0)
1058 				goto file_err;
1059 			goto data_err;
1060 		}
1061 		reply(226, "Transfer complete.");
1062 		return;
1063 	default:
1064 		transflag = 0;
1065 		reply(550, "Unimplemented TYPE %d in send_data", type);
1066 		return;
1067 	}
1068 
1069 data_err:
1070 	transflag = 0;
1071 	perror_reply(426, "Data connection");
1072 	return;
1073 
1074 file_err:
1075 	transflag = 0;
1076 	perror_reply(551, "Error on input file");
1077 }
1078 
1079 /*
1080  * Transfer data from peer to "outstr" using the appropriate encapulation of
1081  * the data subject to Mode, Structure, and Type.
1082  *
1083  * N.B.: Form isn't handled.
1084  */
1085 static int
1086 receive_data(instr, outstr)
1087 	FILE *instr, *outstr;
1088 {
1089 	int	c, cnt, bare_lfs;
1090 	char	buf[BUFSIZ];
1091 #ifdef __GNUC__
1092 	(void) &bare_lfs;
1093 #endif
1094 
1095 	bare_lfs = 0;
1096 	transflag++;
1097 	if (setjmp(urgcatch)) {
1098 		transflag = 0;
1099 		return (-1);
1100 	}
1101 
1102 	switch (type) {
1103 
1104 	case TYPE_I:
1105 	case TYPE_L:
1106 		while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) {
1107 			if (write(fileno(outstr), buf, cnt) != cnt)
1108 				goto file_err;
1109 			byte_count += cnt;
1110 		}
1111 		if (cnt < 0)
1112 			goto data_err;
1113 		transflag = 0;
1114 		return (0);
1115 
1116 	case TYPE_E:
1117 		reply(553, "TYPE E not implemented.");
1118 		transflag = 0;
1119 		return (-1);
1120 
1121 	case TYPE_A:
1122 		while ((c = getc(instr)) != EOF) {
1123 			byte_count++;
1124 			if (c == '\n')
1125 				bare_lfs++;
1126 			while (c == '\r') {
1127 				if (ferror(outstr))
1128 					goto data_err;
1129 				if ((c = getc(instr)) != '\n') {
1130 					(void) putc ('\r', outstr);
1131 					if (c == '\0' || c == EOF)
1132 						goto contin2;
1133 				}
1134 			}
1135 			(void) putc(c, outstr);
1136 	contin2:	;
1137 		}
1138 		fflush(outstr);
1139 		if (ferror(instr))
1140 			goto data_err;
1141 		if (ferror(outstr))
1142 			goto file_err;
1143 		transflag = 0;
1144 		if (bare_lfs) {
1145 			lreply(226,
1146 		"WARNING! %d bare linefeeds received in ASCII mode",
1147 			    bare_lfs);
1148 		(void)printf("   File may not have transferred correctly.\r\n");
1149 		}
1150 		return (0);
1151 	default:
1152 		reply(550, "Unimplemented TYPE %d in receive_data", type);
1153 		transflag = 0;
1154 		return (-1);
1155 	}
1156 
1157 data_err:
1158 	transflag = 0;
1159 	perror_reply(426, "Data Connection");
1160 	return (-1);
1161 
1162 file_err:
1163 	transflag = 0;
1164 	perror_reply(452, "Error writing file");
1165 	return (-1);
1166 }
1167 
1168 void
1169 statfilecmd(filename)
1170 	char *filename;
1171 {
1172 	FILE *fin;
1173 	int c;
1174 	char line[LINE_MAX];
1175 
1176 	(void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
1177 	fin = ftpd_popen(line, "r", 0);
1178 	lreply(211, "status of %s:", filename);
1179 	while ((c = getc(fin)) != EOF) {
1180 		if (c == '\n') {
1181 			if (ferror(stdout)){
1182 				perror_reply(421, "control connection");
1183 				(void) ftpd_pclose(fin);
1184 				dologout(1);
1185 				/* NOTREACHED */
1186 			}
1187 			if (ferror(fin)) {
1188 				perror_reply(551, filename);
1189 				(void) ftpd_pclose(fin);
1190 				return;
1191 			}
1192 			(void) putc('\r', stdout);
1193 		}
1194 		(void) putc(c, stdout);
1195 	}
1196 	(void) ftpd_pclose(fin);
1197 	reply(211, "End of Status");
1198 }
1199 
1200 void
1201 statcmd()
1202 {
1203 	struct sockaddr_in *sin;
1204 	u_char *a, *p;
1205 
1206 	lreply(211, "%s FTP server status:", hostname);
1207 	lreply(211, "%s", version);
1208 	if (isdigit(remotehost[0]))
1209 		lreply(211, "Connected to %s", remotehost);
1210 	else
1211 		lreply(211, "Connected to %s (%s)", remotehost,
1212 		    inet_ntoa(his_addr.sin_addr));
1213 	if (logged_in) {
1214 		if (guest)
1215 			lreply(211, "Logged in anonymously");
1216 		else
1217 			lreply(211, "Logged in as %s", pw->pw_name);
1218 	} else if (askpasswd)
1219 		lreply(211, "Waiting for password");
1220 	else
1221 		lreply(211, "Waiting for user name");
1222 	printf("211- TYPE: %s", typenames[type]);
1223 	if (type == TYPE_A || type == TYPE_E)
1224 		printf(", FORM: %s", formnames[form]);
1225 	if (type == TYPE_L)
1226 #if NBBY == 8
1227 		printf(" %d", NBBY);
1228 #else
1229 		printf(" %d", bytesize);	/* need definition! */
1230 #endif
1231 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
1232 	    strunames[stru], modenames[mode]);
1233 	if (data != -1)
1234 		lreply(211, "Data connection open");
1235 	else if (pdata != -1) {
1236 		printf("211- in Passive mode");
1237 		sin = &pasv_addr;
1238 		goto printaddr;
1239 	} else if (usedefault == 0) {
1240 		printf("211- PORT");
1241 		sin = &data_dest;
1242 printaddr:
1243 		a = (u_char *) &sin->sin_addr;
1244 		p = (u_char *) &sin->sin_port;
1245 #define UC(b) (((int) b) & 0xff)
1246 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1247 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1248 #undef UC
1249 	} else
1250 		lreply(211, "No data connection");
1251 
1252 	if (logged_in) {
1253 		struct ftpconv *cp;
1254 
1255 		lreply(211, "");
1256 		lreply(211, "Class: %s", curclass.classname);
1257 		if (curclass.display)
1258 			lreply(211, "Display file: %s", curclass.display);
1259 		if (curclass.notify)
1260 			lreply(211, "Notify fileglob: %s", curclass.notify);
1261 		lreply(211, "Idle timeout: %d, maximum timeout: %d",
1262 		    curclass.timeout, curclass.maxtimeout);
1263 		lreply(211, "dele, mkd, rmd, umask, chmod: %sabled",
1264 		    curclass.modify ? "en" : "dis");
1265 		lreply(211, "Umask: %.04o", curclass.umask);
1266 		for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
1267 			if (cp->suffix == NULL || cp->types == NULL ||
1268 			    cp->command == NULL)
1269 				continue;
1270 			lreply(211,
1271 			    "Conversion: %s [%s] disable: %s, command: %s",
1272 			    cp->suffix, cp->types, cp->disable, cp->command);
1273 		}
1274 	}
1275 
1276 	reply(211, "End of status");
1277 }
1278 
1279 void
1280 fatal(s)
1281 	char *s;
1282 {
1283 
1284 	reply(451, "Error in server: %s\n", s);
1285 	reply(221, "Closing connection due to server error.");
1286 	dologout(0);
1287 	/* NOTREACHED */
1288 }
1289 
1290 void
1291 #if __STDC__
1292 reply(int n, const char *fmt, ...)
1293 #else
1294 reply(n, fmt, va_alist)
1295 	int n;
1296 	char *fmt;
1297         va_dcl
1298 #endif
1299 {
1300 	va_list ap;
1301 #if __STDC__
1302 	va_start(ap, fmt);
1303 #else
1304 	va_start(ap);
1305 #endif
1306 	(void)printf("%d ", n);
1307 	(void)vprintf(fmt, ap);
1308 	(void)printf("\r\n");
1309 	(void)fflush(stdout);
1310 	if (debug) {
1311 		syslog(LOG_DEBUG, "<--- %d ", n);
1312 		vsyslog(LOG_DEBUG, fmt, ap);
1313 	}
1314 }
1315 
1316 void
1317 #if __STDC__
1318 lreply(int n, const char *fmt, ...)
1319 #else
1320 lreply(n, fmt, va_alist)
1321 	int n;
1322 	char *fmt;
1323         va_dcl
1324 #endif
1325 {
1326 	va_list ap;
1327 #if __STDC__
1328 	va_start(ap, fmt);
1329 #else
1330 	va_start(ap);
1331 #endif
1332 	(void)printf("%d- ", n);
1333 	(void)vprintf(fmt, ap);
1334 	(void)printf("\r\n");
1335 	(void)fflush(stdout);
1336 	if (debug) {
1337 		syslog(LOG_DEBUG, "<--- %d- ", n);
1338 		vsyslog(LOG_DEBUG, fmt, ap);
1339 	}
1340 }
1341 
1342 static void
1343 ack(s)
1344 	char *s;
1345 {
1346 
1347 	reply(250, "%s command successful.", s);
1348 }
1349 
1350 void
1351 nack(s)
1352 	char *s;
1353 {
1354 
1355 	reply(502, "%s command not implemented.", s);
1356 }
1357 
1358 /* ARGSUSED */
1359 void
1360 yyerror(s)
1361 	char *s;
1362 {
1363 	char *cp;
1364 
1365 	if ((cp = strchr(cbuf,'\n')) != NULL)
1366 		*cp = '\0';
1367 	reply(500, "'%s': command not understood.", cbuf);
1368 }
1369 
1370 void
1371 delete(name)
1372 	char *name;
1373 {
1374 	struct stat st;
1375 
1376 	LOGCMD("delete", name);
1377 	if (stat(name, &st) < 0) {
1378 		perror_reply(550, name);
1379 		return;
1380 	}
1381 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
1382 		if (rmdir(name) < 0) {
1383 			perror_reply(550, name);
1384 			return;
1385 		}
1386 		goto done;
1387 	}
1388 	if (unlink(name) < 0) {
1389 		perror_reply(550, name);
1390 		return;
1391 	}
1392 done:
1393 	ack("DELE");
1394 }
1395 
1396 void
1397 cwd(path)
1398 	char *path;
1399 {
1400 
1401 	if (chdir(path) < 0)
1402 		perror_reply(550, path);
1403 	else {
1404 		show_chdir_messages(250);
1405 		ack("CWD");
1406 	}
1407 }
1408 
1409 void
1410 makedir(name)
1411 	char *name;
1412 {
1413 
1414 	LOGCMD("mkdir", name);
1415 	if (mkdir(name, 0777) < 0)
1416 		perror_reply(550, name);
1417 	else
1418 		reply(257, "MKD command successful.");
1419 }
1420 
1421 void
1422 removedir(name)
1423 	char *name;
1424 {
1425 
1426 	LOGCMD("rmdir", name);
1427 	if (rmdir(name) < 0)
1428 		perror_reply(550, name);
1429 	else
1430 		ack("RMD");
1431 }
1432 
1433 void
1434 pwd()
1435 {
1436 	char path[MAXPATHLEN + 1];
1437 
1438 	if (getwd(path) == (char *)NULL)
1439 		reply(550, "%s.", path);
1440 	else
1441 		reply(257, "\"%s\" is current directory.", path);
1442 }
1443 
1444 char *
1445 renamefrom(name)
1446 	char *name;
1447 {
1448 	struct stat st;
1449 
1450 	if (stat(name, &st) < 0) {
1451 		perror_reply(550, name);
1452 		return ((char *)0);
1453 	}
1454 	reply(350, "File exists, ready for destination name");
1455 	return (name);
1456 }
1457 
1458 void
1459 renamecmd(from, to)
1460 	char *from, *to;
1461 {
1462 
1463 	LOGCMD2("rename", from, to);
1464 	if (rename(from, to) < 0)
1465 		perror_reply(550, "rename");
1466 	else
1467 		ack("RNTO");
1468 }
1469 
1470 static void
1471 dolog(sin)
1472 	struct sockaddr_in *sin;
1473 {
1474 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1475 		sizeof(struct in_addr), AF_INET);
1476 
1477 	if (hp)
1478 		(void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
1479 	else
1480 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1481 		    sizeof(remotehost));
1482 #ifdef HASSETPROCTITLE
1483 	snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
1484 	setproctitle(proctitle);
1485 #endif /* HASSETPROCTITLE */
1486 
1487 	if (logging)
1488 		syslog(LOG_INFO, "connection from %s", remotehost);
1489 }
1490 
1491 /*
1492  * Record logout in wtmp file
1493  * and exit with supplied status.
1494  */
1495 void
1496 dologout(status)
1497 	int status;
1498 {
1499 	/*
1500 	* Prevent reception of SIGURG from resulting in a resumption
1501 	* back to the main program loop.
1502 	*/
1503 	transflag = 0;
1504 
1505 	if (logged_in) {
1506 		(void) seteuid((uid_t)0);
1507 		logwtmp(ttyline, "", "");
1508 #if defined(KERBEROS)
1509 		if (!notickets && krbtkfile_env)
1510 			unlink(krbtkfile_env);
1511 #endif
1512 	}
1513 	/* beware of flushing buffers after a SIGPIPE */
1514 	_exit(status);
1515 }
1516 
1517 static void
1518 myoob(signo)
1519 	int signo;
1520 {
1521 	char *cp;
1522 
1523 	/* only process if transfer occurring */
1524 	if (!transflag)
1525 		return;
1526 	cp = tmpline;
1527 	if (getline(cp, 7, stdin) == NULL) {
1528 		reply(221, "You could at least say goodbye.");
1529 		dologout(0);
1530 	}
1531 	upper(cp);
1532 	if (strcmp(cp, "ABOR\r\n") == 0) {
1533 		tmpline[0] = '\0';
1534 		reply(426, "Transfer aborted. Data connection closed.");
1535 		reply(226, "Abort successful");
1536 		longjmp(urgcatch, 1);
1537 	}
1538 	if (strcmp(cp, "STAT\r\n") == 0) {
1539 		if (file_size != (off_t) -1)
1540 			reply(213, "Status: %qd of %qd bytes transferred",
1541 			    byte_count, file_size);
1542 		else
1543 			reply(213, "Status: %qd bytes transferred", byte_count);
1544 	}
1545 }
1546 
1547 /*
1548  * Note: a response of 425 is not mentioned as a possible response to
1549  *	the PASV command in RFC959. However, it has been blessed as
1550  *	a legitimate response by Jon Postel in a telephone conversation
1551  *	with Rick Adams on 25 Jan 89.
1552  */
1553 void
1554 passive()
1555 {
1556 	int len;
1557 	char *p, *a;
1558 
1559 	pdata = socket(AF_INET, SOCK_STREAM, 0);
1560 	if (pdata < 0 || !logged_in) {
1561 		perror_reply(425, "Can't open passive connection");
1562 		return;
1563 	}
1564 	pasv_addr = ctrl_addr;
1565 	pasv_addr.sin_port = 0;
1566 	(void) seteuid((uid_t)0);
1567 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1568 		(void) seteuid((uid_t)pw->pw_uid);
1569 		goto pasv_error;
1570 	}
1571 	(void) seteuid((uid_t)pw->pw_uid);
1572 	len = sizeof(pasv_addr);
1573 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1574 		goto pasv_error;
1575 	if (listen(pdata, 1) < 0)
1576 		goto pasv_error;
1577 	a = (char *) &pasv_addr.sin_addr;
1578 	p = (char *) &pasv_addr.sin_port;
1579 
1580 #define UC(b) (((int) b) & 0xff)
1581 
1582 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1583 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1584 	return;
1585 
1586 pasv_error:
1587 	(void) close(pdata);
1588 	pdata = -1;
1589 	perror_reply(425, "Can't open passive connection");
1590 	return;
1591 }
1592 
1593 /*
1594  * Generate unique name for file with basename "local".
1595  * The file named "local" is already known to exist.
1596  * Generates failure reply on error.
1597  */
1598 static char *
1599 gunique(local)
1600 	char *local;
1601 {
1602 	static char new[MAXPATHLEN];
1603 	struct stat st;
1604 	int count;
1605 	char *cp;
1606 
1607 	cp = strrchr(local, '/');
1608 	if (cp)
1609 		*cp = '\0';
1610 	if (stat(cp ? local : ".", &st) < 0) {
1611 		perror_reply(553, cp ? local : ".");
1612 		return ((char *) 0);
1613 	}
1614 	if (cp)
1615 		*cp = '/';
1616 	(void) strcpy(new, local);
1617 	cp = new + strlen(new);
1618 	*cp++ = '.';
1619 	for (count = 1; count < 100; count++) {
1620 		(void)sprintf(cp, "%d", count);
1621 		if (stat(new, &st) < 0)
1622 			return (new);
1623 	}
1624 	reply(452, "Unique file name cannot be created.");
1625 	return (NULL);
1626 }
1627 
1628 /*
1629  * Format and send reply containing system error number.
1630  */
1631 void
1632 perror_reply(code, string)
1633 	int code;
1634 	char *string;
1635 {
1636 
1637 	reply(code, "%s: %s.", string, strerror(errno));
1638 }
1639 
1640 static char *onefile[] = {
1641 	"",
1642 	0
1643 };
1644 
1645 void
1646 send_file_list(whichf)
1647 	char *whichf;
1648 {
1649 	struct stat st;
1650 	DIR *dirp = NULL;
1651 	struct dirent *dir;
1652 	FILE *dout = NULL;
1653 	char **dirlist, *dirname;
1654 	int simple = 0;
1655 	int freeglob = 0;
1656 	glob_t gl;
1657 #ifdef __GNUC__
1658 	(void) &dout;
1659 	(void) &dirlist;
1660 	(void) &simple;
1661 	(void) &freeglob;
1662 #endif
1663 
1664 	if (strpbrk(whichf, "~{[*?") != NULL) {
1665 		int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1666 
1667 		memset(&gl, 0, sizeof(gl));
1668 		freeglob = 1;
1669 		if (glob(whichf, flags, 0, &gl)) {
1670 			reply(550, "not found");
1671 			goto out;
1672 		} else if (gl.gl_pathc == 0) {
1673 			errno = ENOENT;
1674 			perror_reply(550, whichf);
1675 			goto out;
1676 		}
1677 		dirlist = gl.gl_pathv;
1678 	} else {
1679 		onefile[0] = whichf;
1680 		dirlist = onefile;
1681 		simple = 1;
1682 	}
1683 
1684 	if (setjmp(urgcatch)) {
1685 		transflag = 0;
1686 		goto out;
1687 	}
1688 	while ((dirname = *dirlist++) != NULL) {
1689 		if (stat(dirname, &st) < 0) {
1690 			/*
1691 			 * If user typed "ls -l", etc, and the client
1692 			 * used NLST, do what the user meant.
1693 			 */
1694 			if (dirname[0] == '-' && *dirlist == NULL &&
1695 			    transflag == 0) {
1696 				retrieve("/bin/ls %s", dirname);
1697 				goto out;
1698 			}
1699 			perror_reply(550, whichf);
1700 			if (dout != NULL) {
1701 				(void) fclose(dout);
1702 				transflag = 0;
1703 				data = -1;
1704 				pdata = -1;
1705 			}
1706 			goto out;
1707 		}
1708 
1709 		if (S_ISREG(st.st_mode)) {
1710 			if (dout == NULL) {
1711 				dout = dataconn("file list", (off_t)-1, "w");
1712 				if (dout == NULL)
1713 					goto out;
1714 				transflag++;
1715 			}
1716 			fprintf(dout, "%s%s\n", dirname,
1717 				type == TYPE_A ? "\r" : "");
1718 			byte_count += strlen(dirname) + 1;
1719 			continue;
1720 		} else if (!S_ISDIR(st.st_mode))
1721 			continue;
1722 
1723 		if ((dirp = opendir(dirname)) == NULL)
1724 			continue;
1725 
1726 		while ((dir = readdir(dirp)) != NULL) {
1727 			char nbuf[MAXPATHLEN];
1728 
1729 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1730 				continue;
1731 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1732 			    dir->d_namlen == 2)
1733 				continue;
1734 
1735 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1736 
1737 			/*
1738 			 * We have to do a stat to insure it's
1739 			 * not a directory or special file.
1740 			 */
1741 			if (simple || (stat(nbuf, &st) == 0 &&
1742 			    S_ISREG(st.st_mode))) {
1743 				if (dout == NULL) {
1744 					dout = dataconn("file list", (off_t)-1,
1745 						"w");
1746 					if (dout == NULL)
1747 						goto out;
1748 					transflag++;
1749 				}
1750 				if (nbuf[0] == '.' && nbuf[1] == '/')
1751 					fprintf(dout, "%s%s\n", &nbuf[2],
1752 						type == TYPE_A ? "\r" : "");
1753 				else
1754 					fprintf(dout, "%s%s\n", nbuf,
1755 						type == TYPE_A ? "\r" : "");
1756 				byte_count += strlen(nbuf) + 1;
1757 			}
1758 		}
1759 		(void) closedir(dirp);
1760 	}
1761 
1762 	if (dout == NULL)
1763 		reply(550, "No files found.");
1764 	else if (ferror(dout) != 0)
1765 		perror_reply(550, "Data connection");
1766 	else
1767 		reply(226, "Transfer complete.");
1768 
1769 	transflag = 0;
1770 	if (dout != NULL)
1771 		(void) fclose(dout);
1772 	data = -1;
1773 	pdata = -1;
1774 out:
1775 	if (freeglob) {
1776 		freeglob = 0;
1777 		globfree(&gl);
1778 	}
1779 }
1780