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