xref: /original-bsd/libexec/ftpd/ftpd.c (revision f3c03cba)
1 /*
2  * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)ftpd.c	5.38 (Berkeley) 08/07/90";
16 #endif /* not lint */
17 
18 /*
19  * FTP server.
20  */
21 #include <sys/param.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25 #include <sys/file.h>
26 #include <sys/wait.h>
27 #include <sys/dir.h>
28 
29 #include <netinet/in.h>
30 #include <netinet/in_systm.h>
31 #include <netinet/ip.h>
32 
33 #define	FTP_NAMES
34 #include <arpa/ftp.h>
35 #include <arpa/inet.h>
36 #include <arpa/telnet.h>
37 
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <signal.h>
41 #include <pwd.h>
42 #include <setjmp.h>
43 #include <netdb.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <varargs.h>
48 #include "pathnames.h"
49 
50 /*
51  * File containing login names
52  * NOT to be used on this machine.
53  * Commonly used to disallow uucp.
54  */
55 extern	int errno;
56 extern	char *crypt();
57 extern	char version[];
58 extern	char *home;		/* pointer to home directory for glob */
59 extern	FILE *ftpd_popen(), *fopen(), *freopen();
60 extern	int  ftpd_pclose(), fclose();
61 extern	char *getline();
62 extern	char cbuf[];
63 extern	off_t restart_point;
64 
65 struct	sockaddr_in ctrl_addr;
66 struct	sockaddr_in data_source;
67 struct	sockaddr_in data_dest;
68 struct	sockaddr_in his_addr;
69 struct	sockaddr_in pasv_addr;
70 
71 int	data;
72 jmp_buf	errcatch, urgcatch;
73 int	logged_in;
74 struct	passwd *pw;
75 int	debug;
76 int	timeout = 900;    /* timeout after 15 minutes of inactivity */
77 int	maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
78 int	logging;
79 int	guest;
80 int	type;
81 int	form;
82 int	stru;			/* avoid C keyword */
83 int	mode;
84 int	usedefault = 1;		/* for data transfers */
85 int	pdata = -1;		/* for passive mode */
86 int	transflag;
87 off_t	file_size;
88 off_t	byte_count;
89 #if !defined(CMASK) || CMASK == 0
90 #undef CMASK
91 #define CMASK 027
92 #endif
93 int	defumask = CMASK;		/* default umask value */
94 char	tmpline[7];
95 char	hostname[MAXHOSTNAMELEN];
96 char	remotehost[MAXHOSTNAMELEN];
97 
98 /*
99  * Timeout intervals for retrying connections
100  * to hosts that don't accept PORT cmds.  This
101  * is a kludge, but given the problems with TCP...
102  */
103 #define	SWAITMAX	90	/* wait at most 90 seconds */
104 #define	SWAITINT	5	/* interval between retries */
105 
106 int	swaitmax = SWAITMAX;
107 int	swaitint = SWAITINT;
108 
109 int	lostconn();
110 int	myoob();
111 FILE	*getdatasock(), *dataconn();
112 
113 #ifdef SETPROCTITLE
114 char	**Argv = NULL;		/* pointer to argument vector */
115 char	*LastArgv = NULL;	/* end of argv */
116 char	proctitle[BUFSIZ];	/* initial part of title */
117 #endif /* SETPROCTITLE */
118 
119 main(argc, argv, envp)
120 	int argc;
121 	char *argv[];
122 	char **envp;
123 {
124 	int addrlen, on = 1, tos;
125 	char *cp;
126 
127 	/*
128 	 * LOG_NDELAY sets up the logging connection immediately,
129 	 * necessary for anonymous ftp's that chroot and can't do it later.
130 	 */
131 	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
132 	addrlen = sizeof (his_addr);
133 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
134 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
135 		exit(1);
136 	}
137 	addrlen = sizeof (ctrl_addr);
138 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
139 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
140 		exit(1);
141 	}
142 #ifdef IP_TOS
143 	tos = IPTOS_LOWDELAY;
144 	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
145 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
146 #endif
147 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
148 	debug = 0;
149 #ifdef SETPROCTITLE
150 	/*
151 	 *  Save start and extent of argv for setproctitle.
152 	 */
153 	Argv = argv;
154 	while (*envp)
155 		envp++;
156 	LastArgv = envp[-1] + strlen(envp[-1]);
157 #endif /* SETPROCTITLE */
158 
159 	argc--, argv++;
160 	while (argc > 0 && *argv[0] == '-') {
161 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
162 
163 		case 'v':
164 			debug = 1;
165 			break;
166 
167 		case 'd':
168 			debug = 1;
169 			break;
170 
171 		case 'l':
172 			logging = 1;
173 			break;
174 
175 		case 't':
176 			timeout = atoi(++cp);
177 			if (maxtimeout < timeout)
178 				maxtimeout = timeout;
179 			goto nextopt;
180 
181 		case 'T':
182 			maxtimeout = atoi(++cp);
183 			if (timeout > maxtimeout)
184 				timeout = maxtimeout;
185 			goto nextopt;
186 
187 		case 'u':
188 		    {
189 			int val = 0;
190 
191 			while (*++cp && *cp >= '0' && *cp <= '9')
192 				val = val*8 + *cp - '0';
193 			if (*cp)
194 				fprintf(stderr, "ftpd: Bad value for -u\n");
195 			else
196 				defumask = val;
197 			goto nextopt;
198 		    }
199 
200 		default:
201 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
202 			     *cp);
203 			break;
204 		}
205 nextopt:
206 		argc--, argv++;
207 	}
208 	(void) freopen(_PATH_DEVNULL, "w", stderr);
209 	(void) signal(SIGPIPE, lostconn);
210 	(void) signal(SIGCHLD, SIG_IGN);
211 	if ((int)signal(SIGURG, myoob) < 0)
212 		syslog(LOG_ERR, "signal: %m");
213 
214 	/* Try to handle urgent data inline */
215 #ifdef SO_OOBINLINE
216 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
217 		syslog(LOG_ERR, "setsockopt: %m");
218 #endif
219 
220 #ifdef	F_SETOWN
221 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
222 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
223 #endif
224 	dolog(&his_addr);
225 	/*
226 	 * Set up default state
227 	 */
228 	data = -1;
229 	type = TYPE_A;
230 	form = FORM_N;
231 	stru = STRU_F;
232 	mode = MODE_S;
233 	tmpline[0] = '\0';
234 	(void) gethostname(hostname, sizeof (hostname));
235 	reply(220, "%s FTP server (%s) ready.", hostname, version);
236 	(void) setjmp(errcatch);
237 	for (;;)
238 		(void) yyparse();
239 	/* NOTREACHED */
240 }
241 
242 lostconn()
243 {
244 
245 	if (debug)
246 		syslog(LOG_DEBUG, "lost connection");
247 	dologout(-1);
248 }
249 
250 static char ttyline[20];
251 
252 /*
253  * Helper function for sgetpwnam().
254  */
255 char *
256 sgetsave(s)
257 	char *s;
258 {
259 	char *malloc();
260 	char *new = malloc((unsigned) strlen(s) + 1);
261 
262 	if (new == NULL) {
263 		perror_reply(421, "Local resource failure: malloc");
264 		dologout(1);
265 		/* NOTREACHED */
266 	}
267 	(void) strcpy(new, s);
268 	return (new);
269 }
270 
271 /*
272  * Save the result of a getpwnam.  Used for USER command, since
273  * the data returned must not be clobbered by any other command
274  * (e.g., globbing).
275  */
276 struct passwd *
277 sgetpwnam(name)
278 	char *name;
279 {
280 	static struct passwd save;
281 	register struct passwd *p;
282 	char *sgetsave();
283 
284 	if ((p = getpwnam(name)) == NULL)
285 		return (p);
286 	if (save.pw_name) {
287 		free(save.pw_name);
288 		free(save.pw_passwd);
289 		free(save.pw_gecos);
290 		free(save.pw_dir);
291 		free(save.pw_shell);
292 	}
293 	save = *p;
294 	save.pw_name = sgetsave(p->pw_name);
295 	save.pw_passwd = sgetsave(p->pw_passwd);
296 	save.pw_gecos = sgetsave(p->pw_gecos);
297 	save.pw_dir = sgetsave(p->pw_dir);
298 	save.pw_shell = sgetsave(p->pw_shell);
299 	return (&save);
300 }
301 
302 int login_attempts;		/* number of failed login attempts */
303 int askpasswd;			/* had user command, ask for passwd */
304 
305 /*
306  * USER command.
307  * Sets global passwd pointer pw if named account exists and is acceptable;
308  * sets askpasswd if a PASS command is expected.  If logged in previously,
309  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
310  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
311  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
312  * requesting login privileges.  Disallow anyone who does not have a standard
313  * shell as returned by getusershell().  Disallow anyone mentioned in the file
314  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
315  */
316 user(name)
317 	char *name;
318 {
319 	register char *cp;
320 	char *shell;
321 	char *getusershell();
322 
323 	if (logged_in) {
324 		if (guest) {
325 			reply(530, "Can't change user from guest login.");
326 			return;
327 		}
328 		end_login();
329 	}
330 
331 	guest = 0;
332 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
333 		if (checkuser("ftp") || checkuser("anonymous"))
334 			reply(530, "User %s access denied.", name);
335 		else if ((pw = sgetpwnam("ftp")) != NULL) {
336 			guest = 1;
337 			askpasswd = 1;
338 			reply(331, "Guest login ok, send ident as password.");
339 		} else
340 			reply(530, "User %s unknown.", name);
341 		return;
342 	}
343 	if (pw = sgetpwnam(name)) {
344 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
345 			shell = _PATH_BSHELL;
346 		while ((cp = getusershell()) != NULL)
347 			if (strcmp(cp, shell) == 0)
348 				break;
349 		endusershell();
350 		if (cp == NULL || checkuser(name)) {
351 			reply(530, "User %s access denied.", name);
352 			if (logging)
353 				syslog(LOG_NOTICE,
354 				    "FTP LOGIN REFUSED FROM %s, %s",
355 				    remotehost, name);
356 			pw = (struct passwd *) NULL;
357 			return;
358 		}
359 	}
360 	reply(331, "Password required for %s.", name);
361 	askpasswd = 1;
362 	/*
363 	 * Delay before reading passwd after first failed
364 	 * attempt to slow down passwd-guessing programs.
365 	 */
366 	if (login_attempts)
367 		sleep((unsigned) login_attempts);
368 }
369 
370 /*
371  * Check if a user is in the file _PATH_FTPUSERS
372  */
373 checkuser(name)
374 	char *name;
375 {
376 	register FILE *fd;
377 	register char *p;
378 	char line[BUFSIZ];
379 
380 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
381 		while (fgets(line, sizeof(line), fd) != NULL)
382 			if ((p = index(line, '\n')) != NULL) {
383 				*p = '\0';
384 				if (line[0] == '#')
385 					continue;
386 				if (strcmp(line, name) == 0)
387 					return (1);
388 			}
389 		(void) fclose(fd);
390 	}
391 	return (0);
392 }
393 
394 /*
395  * Terminate login as previous user, if any, resetting state;
396  * used when USER command is given or login fails.
397  */
398 end_login()
399 {
400 
401 	(void) seteuid((uid_t)0);
402 	if (logged_in)
403 		logwtmp(ttyline, "", "");
404 	pw = NULL;
405 	logged_in = 0;
406 	guest = 0;
407 }
408 
409 pass(passwd)
410 	char *passwd;
411 {
412 	char *xpasswd, *salt;
413 
414 	if (logged_in || askpasswd == 0) {
415 		reply(503, "Login with USER first.");
416 		return;
417 	}
418 	askpasswd = 0;
419 	if (!guest) {		/* "ftp" is only account allowed no password */
420 		if (pw == NULL)
421 			salt = "xx";
422 		else
423 			salt = pw->pw_passwd;
424 		xpasswd = crypt(passwd, salt);
425 		/* The strcmp does not catch null passwords! */
426 		if (pw == NULL || *pw->pw_passwd == '\0' ||
427 		    strcmp(xpasswd, pw->pw_passwd)) {
428 			reply(530, "Login incorrect.");
429 			pw = NULL;
430 			if (login_attempts++ >= 5) {
431 				syslog(LOG_NOTICE,
432 				    "repeated login failures from %s",
433 				    remotehost);
434 				exit(0);
435 			}
436 			return;
437 		}
438 	}
439 	login_attempts = 0;		/* this time successful */
440 	(void) setegid((gid_t)pw->pw_gid);
441 	(void) initgroups(pw->pw_name, pw->pw_gid);
442 
443 	/* open wtmp before chroot */
444 	(void)sprintf(ttyline, "ftp%d", getpid());
445 	logwtmp(ttyline, pw->pw_name, remotehost);
446 	logged_in = 1;
447 
448 	if (guest) {
449 		/*
450 		 * We MUST do a chdir() after the chroot. Otherwise
451 		 * the old current directory will be accessible as "."
452 		 * outside the new root!
453 		 */
454 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
455 			reply(550, "Can't set guest privileges.");
456 			goto bad;
457 		}
458 	} else if (chdir(pw->pw_dir) < 0) {
459 		if (chdir("/") < 0) {
460 			reply(530, "User %s: can't change directory to %s.",
461 			    pw->pw_name, pw->pw_dir);
462 			goto bad;
463 		} else
464 			lreply(230, "No directory! Logging in with home=/");
465 	}
466 	if (seteuid((uid_t)pw->pw_uid) < 0) {
467 		reply(550, "Can't set uid.");
468 		goto bad;
469 	}
470 	if (guest) {
471 		reply(230, "Guest login ok, access restrictions apply.");
472 #ifdef SETPROCTITLE
473 		sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
474 		    sizeof(proctitle) - sizeof(remotehost) -
475 		    sizeof(": anonymous/"), passwd);
476 		setproctitle(proctitle);
477 #endif /* SETPROCTITLE */
478 		if (logging)
479 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
480 			    remotehost, passwd);
481 	} else {
482 		reply(230, "User %s logged in.", pw->pw_name);
483 #ifdef SETPROCTITLE
484 		sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
485 		setproctitle(proctitle);
486 #endif /* SETPROCTITLE */
487 		if (logging)
488 			syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
489 			    remotehost, pw->pw_name);
490 	}
491 	home = pw->pw_dir;		/* home dir for globbing */
492 	(void) umask(defumask);
493 	return;
494 bad:
495 	/* Forget all about it... */
496 	end_login();
497 }
498 
499 retrieve(cmd, name)
500 	char *cmd, *name;
501 {
502 	FILE *fin, *dout;
503 	struct stat st;
504 	int (*closefunc)();
505 
506 	if (cmd == 0) {
507 		fin = fopen(name, "r"), closefunc = fclose;
508 		st.st_size = 0;
509 	} else {
510 		char line[BUFSIZ];
511 
512 		(void) sprintf(line, cmd, name), name = line;
513 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
514 		st.st_size = -1;
515 		st.st_blksize = BUFSIZ;
516 	}
517 	if (fin == NULL) {
518 		if (errno != 0)
519 			perror_reply(550, name);
520 		return;
521 	}
522 	if (cmd == 0 &&
523 	    (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
524 		reply(550, "%s: not a plain file.", name);
525 		goto done;
526 	}
527 	if (restart_point) {
528 		if (type == TYPE_A) {
529 			register int i, n, c;
530 
531 			n = restart_point;
532 			i = 0;
533 			while (i++ < n) {
534 				if ((c=getc(fin)) == EOF) {
535 					perror_reply(550, name);
536 					goto done;
537 				}
538 				if (c == '\n')
539 					i++;
540 			}
541 		} else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
542 			perror_reply(550, name);
543 			goto done;
544 		}
545 	}
546 	dout = dataconn(name, st.st_size, "w");
547 	if (dout == NULL)
548 		goto done;
549 	send_data(fin, dout, st.st_blksize);
550 	(void) fclose(dout);
551 	data = -1;
552 	pdata = -1;
553 done:
554 	(*closefunc)(fin);
555 }
556 
557 store(name, mode, unique)
558 	char *name, *mode;
559 	int unique;
560 {
561 	FILE *fout, *din;
562 	struct stat st;
563 	int (*closefunc)();
564 	char *gunique();
565 
566 	if (unique && stat(name, &st) == 0 &&
567 	    (name = gunique(name)) == NULL)
568 		return;
569 
570 	if (restart_point)
571 		mode = "r+w";
572 	fout = fopen(name, mode);
573 	closefunc = fclose;
574 	if (fout == NULL) {
575 		perror_reply(553, name);
576 		return;
577 	}
578 	if (restart_point) {
579 		if (type == TYPE_A) {
580 			register int i, n, c;
581 
582 			n = restart_point;
583 			i = 0;
584 			while (i++ < n) {
585 				if ((c=getc(fout)) == EOF) {
586 					perror_reply(550, name);
587 					goto done;
588 				}
589 				if (c == '\n')
590 					i++;
591 			}
592 			/*
593 			 * We must do this seek to "current" position
594 			 * because we are changing from reading to
595 			 * writing.
596 			 */
597 			if (fseek(fout, 0L, L_INCR) < 0) {
598 				perror_reply(550, name);
599 				goto done;
600 			}
601 		} else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
602 			perror_reply(550, name);
603 			goto done;
604 		}
605 	}
606 	din = dataconn(name, (off_t)-1, "r");
607 	if (din == NULL)
608 		goto done;
609 	if (receive_data(din, fout) == 0) {
610 		if (unique)
611 			reply(226, "Transfer complete (unique file name:%s).",
612 			    name);
613 		else
614 			reply(226, "Transfer complete.");
615 	}
616 	(void) fclose(din);
617 	data = -1;
618 	pdata = -1;
619 done:
620 	(*closefunc)(fout);
621 }
622 
623 FILE *
624 getdatasock(mode)
625 	char *mode;
626 {
627 	int s, on = 1, tries;
628 
629 	if (data >= 0)
630 		return (fdopen(data, mode));
631 	s = socket(AF_INET, SOCK_STREAM, 0);
632 	if (s < 0)
633 		return (NULL);
634 	(void) seteuid((uid_t)0);
635 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
636 	    (char *) &on, sizeof (on)) < 0)
637 		goto bad;
638 	/* anchor socket to avoid multi-homing problems */
639 	data_source.sin_family = AF_INET;
640 	data_source.sin_addr = ctrl_addr.sin_addr;
641 	for (tries = 1; ; tries++) {
642 		if (bind(s, (struct sockaddr *)&data_source,
643 		    sizeof (data_source)) >= 0)
644 			break;
645 		if (errno != EADDRINUSE || tries > 10)
646 			goto bad;
647 		sleep(tries);
648 	}
649 	(void) seteuid((uid_t)pw->pw_uid);
650 #ifdef IP_TOS
651 	on = IPTOS_THROUGHPUT;
652 	if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
653 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
654 #endif
655 	return (fdopen(s, mode));
656 bad:
657 	(void) seteuid((uid_t)pw->pw_uid);
658 	(void) close(s);
659 	return (NULL);
660 }
661 
662 FILE *
663 dataconn(name, size, mode)
664 	char *name;
665 	off_t size;
666 	char *mode;
667 {
668 	char sizebuf[32];
669 	FILE *file;
670 	int retry = 0, tos;
671 
672 	file_size = size;
673 	byte_count = 0;
674 	if (size != (off_t) -1)
675 		(void) sprintf (sizebuf, " (%ld bytes)", size);
676 	else
677 		(void) strcpy(sizebuf, "");
678 	if (pdata >= 0) {
679 		struct sockaddr_in from;
680 		int s, fromlen = sizeof(from);
681 
682 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
683 		if (s < 0) {
684 			reply(425, "Can't open data connection.");
685 			(void) close(pdata);
686 			pdata = -1;
687 			return(NULL);
688 		}
689 		(void) close(pdata);
690 		pdata = s;
691 #ifdef IP_TOS
692 		tos = IPTOS_LOWDELAY;
693 		(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
694 		    sizeof(int));
695 #endif
696 		reply(150, "Opening %s mode data connection for %s%s.",
697 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
698 		return(fdopen(pdata, mode));
699 	}
700 	if (data >= 0) {
701 		reply(125, "Using existing data connection for %s%s.",
702 		    name, sizebuf);
703 		usedefault = 1;
704 		return (fdopen(data, mode));
705 	}
706 	if (usedefault)
707 		data_dest = his_addr;
708 	usedefault = 1;
709 	file = getdatasock(mode);
710 	if (file == NULL) {
711 		reply(425, "Can't create data socket (%s,%d): %s.",
712 		    inet_ntoa(data_source.sin_addr),
713 		    ntohs(data_source.sin_port), strerror(errno));
714 		return (NULL);
715 	}
716 	data = fileno(file);
717 	while (connect(data, (struct sockaddr *)&data_dest,
718 	    sizeof (data_dest)) < 0) {
719 		if (errno == EADDRINUSE && retry < swaitmax) {
720 			sleep((unsigned) swaitint);
721 			retry += swaitint;
722 			continue;
723 		}
724 		perror_reply(425, "Can't build data connection");
725 		(void) fclose(file);
726 		data = -1;
727 		return (NULL);
728 	}
729 	reply(150, "Opening %s mode data connection for %s%s.",
730 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
731 	return (file);
732 }
733 
734 /*
735  * Tranfer the contents of "instr" to
736  * "outstr" peer using the appropriate
737  * encapsulation of the data subject
738  * to Mode, Structure, and Type.
739  *
740  * NB: Form isn't handled.
741  */
742 send_data(instr, outstr, blksize)
743 	FILE *instr, *outstr;
744 	off_t blksize;
745 {
746 	register int c, cnt;
747 	register char *buf;
748 	int netfd, filefd;
749 
750 	transflag++;
751 	if (setjmp(urgcatch)) {
752 		transflag = 0;
753 		return;
754 	}
755 	switch (type) {
756 
757 	case TYPE_A:
758 		while ((c = getc(instr)) != EOF) {
759 			byte_count++;
760 			if (c == '\n') {
761 				if (ferror(outstr))
762 					goto data_err;
763 				(void) putc('\r', outstr);
764 			}
765 			(void) putc(c, outstr);
766 		}
767 		fflush(outstr);
768 		transflag = 0;
769 		if (ferror(instr))
770 			goto file_err;
771 		if (ferror(outstr))
772 			goto data_err;
773 		reply(226, "Transfer complete.");
774 		return;
775 
776 	case TYPE_I:
777 	case TYPE_L:
778 		if ((buf = malloc((u_int)blksize)) == NULL) {
779 			transflag = 0;
780 			perror_reply(451, "Local resource failure: malloc");
781 			return;
782 		}
783 		netfd = fileno(outstr);
784 		filefd = fileno(instr);
785 		while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
786 		    write(netfd, buf, cnt) == cnt)
787 			byte_count += cnt;
788 		transflag = 0;
789 		(void)free(buf);
790 		if (cnt != 0) {
791 			if (cnt < 0)
792 				goto file_err;
793 			goto data_err;
794 		}
795 		reply(226, "Transfer complete.");
796 		return;
797 	default:
798 		transflag = 0;
799 		reply(550, "Unimplemented TYPE %d in send_data", type);
800 		return;
801 	}
802 
803 data_err:
804 	transflag = 0;
805 	perror_reply(426, "Data connection");
806 	return;
807 
808 file_err:
809 	transflag = 0;
810 	perror_reply(551, "Error on input file");
811 }
812 
813 /*
814  * Transfer data from peer to
815  * "outstr" using the appropriate
816  * encapulation of the data subject
817  * to Mode, Structure, and Type.
818  *
819  * N.B.: Form isn't handled.
820  */
821 receive_data(instr, outstr)
822 	FILE *instr, *outstr;
823 {
824 	register int c;
825 	int cnt, bare_lfs = 0;
826 	char buf[BUFSIZ];
827 
828 	transflag++;
829 	if (setjmp(urgcatch)) {
830 		transflag = 0;
831 		return (-1);
832 	}
833 	switch (type) {
834 
835 	case TYPE_I:
836 	case TYPE_L:
837 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
838 			if (write(fileno(outstr), buf, cnt) != cnt)
839 				goto file_err;
840 			byte_count += cnt;
841 		}
842 		if (cnt < 0)
843 			goto data_err;
844 		transflag = 0;
845 		return (0);
846 
847 	case TYPE_E:
848 		reply(553, "TYPE E not implemented.");
849 		transflag = 0;
850 		return (-1);
851 
852 	case TYPE_A:
853 		while ((c = getc(instr)) != EOF) {
854 			byte_count++;
855 			if (c == '\n')
856 				bare_lfs++;
857 			while (c == '\r') {
858 				if (ferror(outstr))
859 					goto data_err;
860 				if ((c = getc(instr)) != '\n') {
861 					(void) putc ('\r', outstr);
862 					if (c == '\0' || c == EOF)
863 						goto contin2;
864 				}
865 			}
866 			(void) putc(c, outstr);
867 	contin2:	;
868 		}
869 		fflush(outstr);
870 		if (ferror(instr))
871 			goto data_err;
872 		if (ferror(outstr))
873 			goto file_err;
874 		transflag = 0;
875 		if (bare_lfs) {
876 			lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
877 			printf("   File may not have transferred correctly.\r\n");
878 		}
879 		return (0);
880 	default:
881 		reply(550, "Unimplemented TYPE %d in receive_data", type);
882 		transflag = 0;
883 		return (-1);
884 	}
885 
886 data_err:
887 	transflag = 0;
888 	perror_reply(426, "Data Connection");
889 	return (-1);
890 
891 file_err:
892 	transflag = 0;
893 	perror_reply(452, "Error writing file");
894 	return (-1);
895 }
896 
897 statfilecmd(filename)
898 	char *filename;
899 {
900 	char line[BUFSIZ];
901 	FILE *fin;
902 	int c;
903 
904 	(void) sprintf(line, "/bin/ls -lgA %s", filename);
905 	fin = ftpd_popen(line, "r");
906 	lreply(211, "status of %s:", filename);
907 	while ((c = getc(fin)) != EOF) {
908 		if (c == '\n') {
909 			if (ferror(stdout)){
910 				perror_reply(421, "control connection");
911 				(void) ftpd_pclose(fin);
912 				dologout(1);
913 				/* NOTREACHED */
914 			}
915 			if (ferror(fin)) {
916 				perror_reply(551, filename);
917 				(void) ftpd_pclose(fin);
918 				return;
919 			}
920 			(void) putc('\r', stdout);
921 		}
922 		(void) putc(c, stdout);
923 	}
924 	(void) ftpd_pclose(fin);
925 	reply(211, "End of Status");
926 }
927 
928 statcmd()
929 {
930 	struct sockaddr_in *sin;
931 	u_char *a, *p;
932 
933 	lreply(211, "%s FTP server status:", hostname, version);
934 	printf("     %s\r\n", version);
935 	printf("     Connected to %s", remotehost);
936 	if (!isdigit(remotehost[0]))
937 		printf(" (%s)", inet_ntoa(his_addr.sin_addr));
938 	printf("\r\n");
939 	if (logged_in) {
940 		if (guest)
941 			printf("     Logged in anonymously\r\n");
942 		else
943 			printf("     Logged in as %s\r\n", pw->pw_name);
944 	} else if (askpasswd)
945 		printf("     Waiting for password\r\n");
946 	else
947 		printf("     Waiting for user name\r\n");
948 	printf("     TYPE: %s", typenames[type]);
949 	if (type == TYPE_A || type == TYPE_E)
950 		printf(", FORM: %s", formnames[form]);
951 	if (type == TYPE_L)
952 #if NBBY == 8
953 		printf(" %d", NBBY);
954 #else
955 		printf(" %d", bytesize);	/* need definition! */
956 #endif
957 	printf("; STRUcture: %s; transfer MODE: %s\r\n",
958 	    strunames[stru], modenames[mode]);
959 	if (data != -1)
960 		printf("     Data connection open\r\n");
961 	else if (pdata != -1) {
962 		printf("     in Passive mode");
963 		sin = &pasv_addr;
964 		goto printaddr;
965 	} else if (usedefault == 0) {
966 		printf("     PORT");
967 		sin = &data_dest;
968 printaddr:
969 		a = (u_char *) &sin->sin_addr;
970 		p = (u_char *) &sin->sin_port;
971 #define UC(b) (((int) b) & 0xff)
972 		printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
973 			UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
974 #undef UC
975 	} else
976 		printf("     No data connection\r\n");
977 	reply(211, "End of status");
978 }
979 
980 fatal(s)
981 	char *s;
982 {
983 	reply(451, "Error in server: %s\n", s);
984 	reply(221, "Closing connection due to server error.");
985 	dologout(0);
986 	/* NOTREACHED */
987 }
988 
989 /* VARARGS2 */
990 reply(n, fmt, p0, p1, p2, p3, p4, p5)
991 	int n;
992 	char *fmt;
993 {
994 	printf("%d ", n);
995 	printf(fmt, p0, p1, p2, p3, p4, p5);
996 	printf("\r\n");
997 	(void)fflush(stdout);
998 	if (debug) {
999 		syslog(LOG_DEBUG, "<--- %d ", n);
1000 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
1001 }
1002 }
1003 
1004 /* VARARGS2 */
1005 lreply(n, fmt, p0, p1, p2, p3, p4, p5)
1006 	int n;
1007 	char *fmt;
1008 {
1009 	printf("%d- ", n);
1010 	printf(fmt, p0, p1, p2, p3, p4, p5);
1011 	printf("\r\n");
1012 	(void)fflush(stdout);
1013 	if (debug) {
1014 		syslog(LOG_DEBUG, "<--- %d- ", n);
1015 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
1016 	}
1017 }
1018 
1019 ack(s)
1020 	char *s;
1021 {
1022 	reply(250, "%s command successful.", s);
1023 }
1024 
1025 nack(s)
1026 	char *s;
1027 {
1028 	reply(502, "%s command not implemented.", s);
1029 }
1030 
1031 /* ARGSUSED */
1032 yyerror(s)
1033 	char *s;
1034 {
1035 	char *cp;
1036 
1037 	if (cp = index(cbuf,'\n'))
1038 		*cp = '\0';
1039 	reply(500, "'%s': command not understood.", cbuf);
1040 }
1041 
1042 delete(name)
1043 	char *name;
1044 {
1045 	struct stat st;
1046 
1047 	if (stat(name, &st) < 0) {
1048 		perror_reply(550, name);
1049 		return;
1050 	}
1051 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
1052 		if (rmdir(name) < 0) {
1053 			perror_reply(550, name);
1054 			return;
1055 		}
1056 		goto done;
1057 	}
1058 	if (unlink(name) < 0) {
1059 		perror_reply(550, name);
1060 		return;
1061 	}
1062 done:
1063 	ack("DELE");
1064 }
1065 
1066 cwd(path)
1067 	char *path;
1068 {
1069 	if (chdir(path) < 0)
1070 		perror_reply(550, path);
1071 	else
1072 		ack("CWD");
1073 }
1074 
1075 makedir(name)
1076 	char *name;
1077 {
1078 	if (mkdir(name, 0777) < 0)
1079 		perror_reply(550, name);
1080 	else
1081 		reply(257, "MKD command successful.");
1082 }
1083 
1084 removedir(name)
1085 	char *name;
1086 {
1087 	if (rmdir(name) < 0)
1088 		perror_reply(550, name);
1089 	else
1090 		ack("RMD");
1091 }
1092 
1093 pwd()
1094 {
1095 	char path[MAXPATHLEN + 1];
1096 	extern char *getwd();
1097 
1098 	if (getwd(path) == (char *)NULL)
1099 		reply(550, "%s.", path);
1100 	else
1101 		reply(257, "\"%s\" is current directory.", path);
1102 }
1103 
1104 char *
1105 renamefrom(name)
1106 	char *name;
1107 {
1108 	struct stat st;
1109 
1110 	if (stat(name, &st) < 0) {
1111 		perror_reply(550, name);
1112 		return ((char *)0);
1113 	}
1114 	reply(350, "File exists, ready for destination name");
1115 	return (name);
1116 }
1117 
1118 renamecmd(from, to)
1119 	char *from, *to;
1120 {
1121 	if (rename(from, to) < 0)
1122 		perror_reply(550, "rename");
1123 	else
1124 		ack("RNTO");
1125 }
1126 
1127 dolog(sin)
1128 	struct sockaddr_in *sin;
1129 {
1130 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1131 		sizeof (struct in_addr), AF_INET);
1132 	time_t t, time();
1133 	extern char *ctime();
1134 
1135 	if (hp)
1136 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
1137 	else
1138 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1139 		    sizeof (remotehost));
1140 #ifdef SETPROCTITLE
1141 	sprintf(proctitle, "%s: connected", remotehost);
1142 	setproctitle(proctitle);
1143 #endif /* SETPROCTITLE */
1144 
1145 	if (logging) {
1146 		t = time((time_t *) 0);
1147 		syslog(LOG_INFO, "connection from %s at %s",
1148 		    remotehost, ctime(&t));
1149 	}
1150 }
1151 
1152 /*
1153  * Record logout in wtmp file
1154  * and exit with supplied status.
1155  */
1156 dologout(status)
1157 	int status;
1158 {
1159 	if (logged_in) {
1160 		(void) seteuid((uid_t)0);
1161 		logwtmp(ttyline, "", "");
1162 	}
1163 	/* beware of flushing buffers after a SIGPIPE */
1164 	_exit(status);
1165 }
1166 
1167 myoob()
1168 {
1169 	char *cp;
1170 
1171 	/* only process if transfer occurring */
1172 	if (!transflag)
1173 		return;
1174 	cp = tmpline;
1175 	if (getline(cp, 7, stdin) == NULL) {
1176 		reply(221, "You could at least say goodbye.");
1177 		dologout(0);
1178 	}
1179 	upper(cp);
1180 	if (strcmp(cp, "ABOR\r\n") == 0) {
1181 		tmpline[0] = '\0';
1182 		reply(426, "Transfer aborted. Data connection closed.");
1183 		reply(226, "Abort successful");
1184 		longjmp(urgcatch, 1);
1185 	}
1186 	if (strcmp(cp, "STAT\r\n") == 0) {
1187 		if (file_size != (off_t) -1)
1188 			reply(213, "Status: %lu of %lu bytes transferred",
1189 			    byte_count, file_size);
1190 		else
1191 			reply(213, "Status: %lu bytes transferred", byte_count);
1192 	}
1193 }
1194 
1195 /*
1196  * Note: a response of 425 is not mentioned as a possible response to
1197  * 	the PASV command in RFC959. However, it has been blessed as
1198  * 	a legitimate response by Jon Postel in a telephone conversation
1199  *	with Rick Adams on 25 Jan 89.
1200  */
1201 passive()
1202 {
1203 	int len;
1204 	register char *p, *a;
1205 
1206 	pdata = socket(AF_INET, SOCK_STREAM, 0);
1207 	if (pdata < 0) {
1208 		perror_reply(425, "Can't open passive connection");
1209 		return;
1210 	}
1211 	pasv_addr = ctrl_addr;
1212 	pasv_addr.sin_port = 0;
1213 	(void) seteuid((uid_t)0);
1214 	if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1215 		(void) seteuid((uid_t)pw->pw_uid);
1216 		goto pasv_error;
1217 	}
1218 	(void) seteuid((uid_t)pw->pw_uid);
1219 	len = sizeof(pasv_addr);
1220 	if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1221 		goto pasv_error;
1222 	if (listen(pdata, 1) < 0)
1223 		goto pasv_error;
1224 	a = (char *) &pasv_addr.sin_addr;
1225 	p = (char *) &pasv_addr.sin_port;
1226 
1227 #define UC(b) (((int) b) & 0xff)
1228 
1229 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1230 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1231 	return;
1232 
1233 pasv_error:
1234 	(void) close(pdata);
1235 	pdata = -1;
1236 	perror_reply(425, "Can't open passive connection");
1237 	return;
1238 }
1239 
1240 /*
1241  * Generate unique name for file with basename "local".
1242  * The file named "local" is already known to exist.
1243  * Generates failure reply on error.
1244  */
1245 char *
1246 gunique(local)
1247 	char *local;
1248 {
1249 	static char new[MAXPATHLEN];
1250 	struct stat st;
1251 	char *cp = rindex(local, '/');
1252 	int count = 0;
1253 
1254 	if (cp)
1255 		*cp = '\0';
1256 	if (stat(cp ? local : ".", &st) < 0) {
1257 		perror_reply(553, cp ? local : ".");
1258 		return((char *) 0);
1259 	}
1260 	if (cp)
1261 		*cp = '/';
1262 	(void) strcpy(new, local);
1263 	cp = new + strlen(new);
1264 	*cp++ = '.';
1265 	for (count = 1; count < 100; count++) {
1266 		(void) sprintf(cp, "%d", count);
1267 		if (stat(new, &st) < 0)
1268 			return(new);
1269 	}
1270 	reply(452, "Unique file name cannot be created.");
1271 	return((char *) 0);
1272 }
1273 
1274 /*
1275  * Format and send reply containing system error number.
1276  */
1277 perror_reply(code, string)
1278 	int code;
1279 	char *string;
1280 {
1281 	reply(code, "%s: %s.", string, strerror(errno));
1282 }
1283 
1284 static char *onefile[] = {
1285 	"",
1286 	0
1287 };
1288 
1289 send_file_list(whichfiles)
1290 	char *whichfiles;
1291 {
1292 	struct stat st;
1293 	DIR *dirp = NULL;
1294 	struct direct *dir;
1295 	FILE *dout = NULL;
1296 	register char **dirlist, *dirname;
1297 	int simple = 0;
1298 	char *strpbrk();
1299 
1300 	if (strpbrk(whichfiles, "~{[*?") != NULL) {
1301 		extern char **glob(), *globerr;
1302 
1303 		globerr = NULL;
1304 		dirlist = glob(whichfiles);
1305 		if (globerr != NULL) {
1306 			reply(550, globerr);
1307 			return;
1308 		} else if (dirlist == NULL) {
1309 			errno = ENOENT;
1310 			perror_reply(550, whichfiles);
1311 			return;
1312 		}
1313 	} else {
1314 		onefile[0] = whichfiles;
1315 		dirlist = onefile;
1316 		simple = 1;
1317 	}
1318 
1319 	if (setjmp(urgcatch)) {
1320 		transflag = 0;
1321 		return;
1322 	}
1323 	while (dirname = *dirlist++) {
1324 		if (stat(dirname, &st) < 0) {
1325 			/*
1326 			 * If user typed "ls -l", etc, and the client
1327 			 * used NLST, do what the user meant.
1328 			 */
1329 			if (dirname[0] == '-' && *dirlist == NULL &&
1330 			    transflag == 0) {
1331 				retrieve("/bin/ls %s", dirname);
1332 				return;
1333 			}
1334 			perror_reply(550, whichfiles);
1335 			if (dout != NULL) {
1336 				(void) fclose(dout);
1337 				transflag = 0;
1338 				data = -1;
1339 				pdata = -1;
1340 			}
1341 			return;
1342 		}
1343 
1344 		if ((st.st_mode&S_IFMT) == S_IFREG) {
1345 			if (dout == NULL) {
1346 				dout = dataconn("file list", (off_t)-1, "w");
1347 				if (dout == NULL)
1348 					return;
1349 				transflag++;
1350 			}
1351 			fprintf(dout, "%s%s\n", dirname,
1352 				type == TYPE_A ? "\r" : "");
1353 			byte_count += strlen(dirname) + 1;
1354 			continue;
1355 		} else if ((st.st_mode&S_IFMT) != S_IFDIR)
1356 			continue;
1357 
1358 		if ((dirp = opendir(dirname)) == NULL)
1359 			continue;
1360 
1361 		while ((dir = readdir(dirp)) != NULL) {
1362 			char nbuf[MAXPATHLEN];
1363 
1364 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1365 				continue;
1366 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1367 			    dir->d_namlen == 2)
1368 				continue;
1369 
1370 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1371 
1372 			/*
1373 			 * We have to do a stat to insure it's
1374 			 * not a directory or special file.
1375 			 */
1376 			if (simple || (stat(nbuf, &st) == 0 &&
1377 			    (st.st_mode&S_IFMT) == S_IFREG)) {
1378 				if (dout == NULL) {
1379 					dout = dataconn("file list", (off_t)-1,
1380 						"w");
1381 					if (dout == NULL)
1382 						return;
1383 					transflag++;
1384 				}
1385 				if (nbuf[0] == '.' && nbuf[1] == '/')
1386 					fprintf(dout, "%s%s\n", &nbuf[2],
1387 						type == TYPE_A ? "\r" : "");
1388 				else
1389 					fprintf(dout, "%s%s\n", nbuf,
1390 						type == TYPE_A ? "\r" : "");
1391 				byte_count += strlen(nbuf) + 1;
1392 			}
1393 		}
1394 		(void) closedir(dirp);
1395 	}
1396 
1397 	if (dout == NULL)
1398 		reply(550, "No files found.");
1399 	else if (ferror(dout) != 0)
1400 		perror_reply(550, "Data connection");
1401 	else
1402 		reply(226, "Transfer complete.");
1403 
1404 	transflag = 0;
1405 	if (dout != NULL)
1406 		(void) fclose(dout);
1407 	data = -1;
1408 	pdata = -1;
1409 }
1410 
1411 #ifdef SETPROCTITLE
1412 /*
1413  * clobber argv so ps will show what we're doing.
1414  * (stolen from sendmail)
1415  * warning, since this is usually started from inetd.conf, it
1416  * often doesn't have much of an environment or arglist to overwrite.
1417  */
1418 
1419 /*VARARGS1*/
1420 setproctitle(fmt, a, b, c)
1421 char *fmt;
1422 {
1423 	register char *p, *bp, ch;
1424 	register int i;
1425 	char buf[BUFSIZ];
1426 
1427 	(void) sprintf(buf, fmt, a, b, c);
1428 
1429 	/* make ps print our process name */
1430 	p = Argv[0];
1431 	*p++ = '-';
1432 
1433 	i = strlen(buf);
1434 	if (i > LastArgv - p - 2) {
1435 		i = LastArgv - p - 2;
1436 		buf[i] = '\0';
1437 	}
1438 	bp = buf;
1439 	while (ch = *bp++)
1440 		if (ch != '\n' && ch != '\r')
1441 			*p++ = ch;
1442 	while (p < LastArgv)
1443 		*p++ = ' ';
1444 }
1445 #endif /* SETPROCTITLE */
1446