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