xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 11653)
1 #ifndef lint
2 static char sccsid[] = "@(#)ftpd.c	4.18 (Berkeley) 03/23/83";
3 #endif
4 
5 /*
6  * FTP server.
7  */
8 #include <sys/param.h>
9 #include <sys/stat.h>
10 #include <sys/ioctl.h>
11 #include <sys/socket.h>
12 
13 #include <netinet/in.h>
14 
15 #include <stdio.h>
16 #include <signal.h>
17 #include <wait.h>
18 #include <pwd.h>
19 #include <setjmp.h>
20 #include <netdb.h>
21 #include <errno.h>
22 
23 #include "ftp.h"
24 
25 /*
26  * File containing login names
27  * NOT to be used on this machine.
28  * Commonly used to disallow uucp.
29  */
30 #define	FTPUSERS	"/etc/ftpusers"
31 
32 extern	int errno;
33 extern	char *sys_errlist[];
34 extern	char *crypt();
35 extern	char version[];
36 extern	char *home;		/* pointer to home directory for glob */
37 extern	FILE *popen(), *fopen();
38 extern	int pclose(), fclose();
39 
40 struct	sockaddr_in ctrl_addr;
41 struct	sockaddr_in data_source;
42 struct	sockaddr_in data_dest;
43 struct	sockaddr_in his_addr;
44 
45 struct	hostent *hp;
46 
47 int	data;
48 jmp_buf	errcatch;
49 int	logged_in;
50 struct	passwd *pw;
51 int	debug;
52 int	timeout;
53 int	logging = 1;
54 int	guest;
55 int	type;
56 int	form;
57 int	stru;			/* avoid C keyword */
58 int	mode;
59 int	usedefault = 1;		/* for data transfers */
60 char	hostname[32];
61 char	*remotehost;
62 struct	servent *sp;
63 
64 /*
65  * Timeout intervals for retrying connections
66  * to hosts that don't accept PORT cmds.  This
67  * is a kludge, but given the problems with TCP...
68  */
69 #define	SWAITMAX	90	/* wait at most 90 seconds */
70 #define	SWAITINT	5	/* interval between retries */
71 
72 int	swaitmax = SWAITMAX;
73 int	swaitint = SWAITINT;
74 
75 int	lostconn();
76 int	reapchild();
77 FILE	*getdatasock(), *dataconn();
78 char	*ntoa();
79 
80 main(argc, argv)
81 	int argc;
82 	char *argv[];
83 {
84 	int ctrl, s, options = 0;
85 	char *cp;
86 
87 	sp = getservbyname("ftp", "tcp");
88 	if (sp == 0) {
89 		fprintf(stderr, "ftpd: ftp/tcp: unknown service\n");
90 		exit(1);
91 	}
92 	ctrl_addr.sin_port = sp->s_port;
93 	data_source.sin_port = htons(ntohs(sp->s_port) - 1);
94 	signal(SIGPIPE, lostconn);
95 	debug = 0;
96 	argc--, argv++;
97 	while (argc > 0 && *argv[0] == '-') {
98 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
99 
100 		case 'v':
101 			debug = 1;
102 			break;
103 
104 		case 'd':
105 			debug = 1;
106 			options |= SO_DEBUG;
107 			break;
108 
109 		case 't':
110 			timeout = atoi(++cp);
111 			goto nextopt;
112 			break;
113 
114 		default:
115 			fprintf(stderr, "Unknown flag -%c ignored.\n", *cp);
116 			break;
117 		}
118 nextopt:
119 		argc--, argv++;
120 	}
121 #ifndef DEBUG
122 	if (fork())
123 		exit(0);
124 	for (s = 0; s < 10; s++)
125 		if (!logging || (s != 2))
126 			(void) close(s);
127 		(void) close(s);
128 	(void) open("/", 0);
129 	(void) dup2(0, 1);
130 	if (!logging)
131 		(void) dup2(0, 2);
132 	{ int tt = open("/dev/tty", 2);
133 	  if (tt > 0) {
134 		ioctl(tt, TIOCNOTTY, 0);
135 		close(tt);
136 	  }
137 	}
138 #endif
139 	while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
140 		perror("ftpd: socket");
141 		sleep(5);
142 	}
143 	if (options & SO_DEBUG)
144 		if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
145 			perror("ftpd: setsockopt (SO_DEBUG)");
146 #ifdef notdef
147 	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
148 		perror("ftpd: setsockopt (SO_KEEPALIVE)");
149 #endif
150 	while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
151 		perror("ftpd: bind");
152 		sleep(5);
153 	}
154 	sigset(SIGCHLD, reapchild);
155 	listen(s, 10);
156 	for (;;) {
157 		int hisaddrlen = sizeof (his_addr);
158 
159 		ctrl = accept(s, &his_addr, &hisaddrlen, 0);
160 		if (ctrl < 0) {
161 			if (errno == EINTR)
162 				continue;
163 			perror("ftpd: accept");
164 			continue;
165 		}
166 		if (fork() == 0) {
167 			signal (SIGCHLD, SIG_IGN);
168 			if (logging)
169 				dolog(&his_addr);
170 			close(s);
171 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
172 			/* do telnet option negotiation here */
173 			/*
174 			 * Set up default state
175 			 */
176 			logged_in = 0;
177 			data = -1;
178 			type = TYPE_A;
179 			form = FORM_N;
180 			stru = STRU_F;
181 			mode = MODE_S;
182 			gethostname(hostname, sizeof (hostname));
183 			reply(220, "%s FTP server (%s) ready.",
184 				hostname, version);
185 			for (;;) {
186 				setjmp(errcatch);
187 				yyparse();
188 			}
189 		}
190 		close(ctrl);
191 	}
192 }
193 
194 reapchild()
195 {
196 	union wait status;
197 
198 	while (wait3(&status, WNOHANG, 0) > 0)
199 		;
200 }
201 
202 lostconn()
203 {
204 
205 	fatal("Connection closed.");
206 }
207 
208 pass(passwd)
209 	char *passwd;
210 {
211 	char *xpasswd, *savestr();
212 	static struct passwd save;
213 
214 	if (logged_in || pw == NULL) {
215 		reply(503, "Login with USER first.");
216 		return;
217 	}
218 	if (!guest) {		/* "ftp" is only account allowed no password */
219 		xpasswd = crypt(passwd, pw->pw_passwd);
220 		if (strcmp(xpasswd, pw->pw_passwd) != 0) {
221 			reply(530, "Login incorrect.");
222 			pw = NULL;
223 			return;
224 		}
225 	}
226 	setegid(pw->pw_gid);
227 	initgroups(pw->pw_name, pw->pw_gid);
228 	if (chdir(pw->pw_dir)) {
229 		reply(550, "User %s: can't change directory to $s.",
230 			pw->pw_name, pw->pw_dir);
231 		goto bad;
232 	}
233 	if (guest && chroot(pw->pw_dir) < 0) {
234 		reply(550, "Can't set guest privileges.");
235 		goto bad;
236 	}
237 	if (!guest)
238 		reply(230, "User %s logged in.", pw->pw_name);
239 	else
240 		reply(230, "Guest login ok, access restrictions apply.");
241 	logged_in = 1;
242 	seteuid(pw->pw_uid);
243 	/*
244 	 * Save everything so globbing doesn't
245 	 * clobber the fields.
246 	 */
247 	save = *pw;
248 	save.pw_name = savestr(pw->pw_name);
249 	save.pw_passwd = savestr(pw->pw_passwd);
250 	save.pw_comment = savestr(pw->pw_comment);
251 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
252 	save.pw_dir = savestr(pw->pw_dir);
253 	save.pw_shell = savestr(pw->pw_shell);
254 	pw = &save;
255 	home = pw->pw_dir;		/* home dir for globbing */
256 	return;
257 bad:
258 	seteuid(0);
259 	pw = NULL;
260 }
261 
262 char *
263 savestr(s)
264 	char *s;
265 {
266 	char *malloc();
267 	char *new = malloc(strlen(s) + 1);
268 
269 	if (new != NULL)
270 		strcpy(new, s);
271 	return (new);
272 }
273 
274 retrieve(cmd, name)
275 	char *cmd, *name;
276 {
277 	FILE *fin, *dout;
278 	struct stat st;
279 	int (*closefunc)();
280 
281 	if (cmd == 0) {
282 #ifdef notdef
283 		/* no remote command execution -- it's a security hole */
284 		if (*name == '|')
285 			fin = popen(name + 1, "r"), closefunc = pclose;
286 		else
287 #endif
288 			fin = fopen(name, "r"), closefunc = fclose;
289 	} else {
290 		char line[BUFSIZ];
291 
292 		sprintf(line, cmd, name), name = line;
293 		fin = popen(line, "r"), closefunc = pclose;
294 	}
295 	if (fin == NULL) {
296 		reply(550, "%s: %s.", name, sys_errlist[errno]);
297 		return;
298 	}
299 	st.st_size = 0;
300 	if (cmd == 0 &&
301 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
302 		reply(550, "%s: not a plain file.", name);
303 		goto done;
304 	}
305 	dout = dataconn(name, st.st_size, "w");
306 	if (dout == NULL)
307 		goto done;
308 	if (send_data(fin, dout) || ferror(dout))
309 		reply(550, "%s: %s.", name, sys_errlist[errno]);
310 	else
311 		reply(226, "Transfer complete.");
312 	fclose(dout), data = -1;
313 done:
314 	(*closefunc)(fin);
315 }
316 
317 store(name, mode)
318 	char *name, *mode;
319 {
320 	FILE *fout, *din;
321 	int (*closefunc)(), dochown = 0;
322 
323 #ifdef notdef
324 	/* no remote command execution -- it's a security hole */
325 	if (name[0] == '|')
326 		fout = popen(&name[1], "w"), closefunc = pclose;
327 	else
328 #endif
329 	{
330 		struct stat st;
331 
332 		if (stat(name, &st) < 0)
333 			dochown++;
334 		fout = fopen(name, mode), closefunc = fclose;
335 	}
336 	if (fout == NULL) {
337 		reply(550, "%s: %s.", name, sys_errlist[errno]);
338 		return;
339 	}
340 	din = dataconn(name, (off_t)-1, "r");
341 	if (din == NULL)
342 		goto done;
343 	if (receive_data(din, fout) || ferror(fout))
344 		reply(550, "%s: %s.", name, sys_errlist[errno]);
345 	else
346 		reply(226, "Transfer complete.");
347 	fclose(din), data = -1;
348 done:
349 	if (dochown)
350 		(void) chown(name, pw->pw_uid, -1);
351 	(*closefunc)(fout);
352 }
353 
354 FILE *
355 getdatasock(mode)
356 	char *mode;
357 {
358 	int s;
359 
360 	if (data >= 0)
361 		return (fdopen(data, mode));
362 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
363 	if (s < 0)
364 		return (NULL);
365 	seteuid(0);
366 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
367 		goto bad;
368 	if (bind(s, &data_source, sizeof (data_source), 0) < 0)
369 		goto bad;
370 	seteuid(pw->pw_uid);
371 	return (fdopen(s, mode));
372 bad:
373 	seteuid(pw->pw_uid);
374 	close(s);
375 	return (NULL);
376 }
377 
378 FILE *
379 dataconn(name, size, mode)
380 	char *name;
381 	off_t size;
382 	char *mode;
383 {
384 	char sizebuf[32];
385 	FILE *file;
386 	int retry = 0;
387 
388 	if (size >= 0)
389 		sprintf (sizebuf, " (%ld bytes)", size);
390 	else
391 		(void) strcpy(sizebuf, "");
392 	if (data >= 0) {
393 		reply(125, "Using existing data connection for %s%s.",
394 		    name, sizebuf);
395 		usedefault = 1;
396 		return (fdopen(data, mode));
397 	}
398 	if (usedefault)
399 		data_dest = his_addr;
400 	usedefault = 1;
401 	file = getdatasock(mode);
402 	if (file == NULL) {
403 		reply(425, "Can't create data socket (%s,%d): %s.",
404 		    ntoa(data_source.sin_addr),
405 		    ntohs(data_source.sin_port),
406 		    sys_errlist[errno]);
407 		return (NULL);
408 	}
409 	reply(150, "Opening data connection for %s (%s,%d)%s.",
410 	    name, ntoa(data_dest.sin_addr.s_addr),
411 	    ntohs(data_dest.sin_port), sizebuf);
412 	data = fileno(file);
413 	while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
414 		if (errno == EADDRINUSE && retry < swaitmax) {
415 			sleep(swaitint);
416 			retry += swaitint;
417 			continue;
418 		}
419 		reply(425, "Can't build data connection: %s.",
420 		    sys_errlist[errno]);
421 		(void) fclose(file);
422 		data = -1;
423 		return (NULL);
424 	}
425 	return (file);
426 }
427 
428 /*
429  * Tranfer the contents of "instr" to
430  * "outstr" peer using the appropriate
431  * encapulation of the date subject
432  * to Mode, Structure, and Type.
433  *
434  * NB: Form isn't handled.
435  */
436 send_data(instr, outstr)
437 	FILE *instr, *outstr;
438 {
439 	register int c;
440 	int netfd, filefd, cnt;
441 	char buf[BUFSIZ];
442 
443 	switch (type) {
444 
445 	case TYPE_A:
446 		while ((c = getc(instr)) != EOF) {
447 			if (c == '\n') {
448 				if (ferror (outstr))
449 					return (1);
450 				putc('\r', outstr);
451 			}
452 			putc(c, outstr);
453 			if (c == '\r')
454 				putc ('\0', outstr);
455 		}
456 		if (ferror (instr) || ferror (outstr))
457 			return (1);
458 		return (0);
459 
460 	case TYPE_I:
461 	case TYPE_L:
462 		netfd = fileno(outstr);
463 		filefd = fileno(instr);
464 
465 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
466 			if (write(netfd, buf, cnt) < 0)
467 				return (1);
468 		return (cnt < 0);
469 	}
470 	reply(504,"Unimplemented TYPE %d in send_data", type);
471 	return (1);
472 }
473 
474 /*
475  * Transfer data from peer to
476  * "outstr" using the appropriate
477  * encapulation of the data subject
478  * to Mode, Structure, and Type.
479  *
480  * N.B.: Form isn't handled.
481  */
482 receive_data(instr, outstr)
483 	FILE *instr, *outstr;
484 {
485 	register int c;
486 	int cnt;
487 	char buf[BUFSIZ];
488 
489 
490 	switch (type) {
491 
492 	case TYPE_I:
493 	case TYPE_L:
494 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
495 			if (write(fileno(outstr), buf, cnt) < 0)
496 				return (1);
497 		return (cnt < 0);
498 
499 	case TYPE_E:
500 		reply(504, "TYPE E not implemented.");
501 		return (1);
502 
503 	case TYPE_A:
504 		while ((c = getc(instr)) != EOF) {
505 			if (c == '\r') {
506 				if (ferror (outstr))
507 					return (1);
508 				if ((c = getc(instr)) != '\n')
509 					putc ('\r', outstr);
510 				if (c == '\0')
511 					continue;
512 			}
513 			putc (c, outstr);
514 		}
515 		if (ferror (instr) || ferror (outstr))
516 			return (1);
517 		return (0);
518 	}
519 	fatal("Unknown type in receive_data.");
520 	/*NOTREACHED*/
521 }
522 
523 fatal(s)
524 	char *s;
525 {
526 	reply(451, "Error in server: %s\n", s);
527 	reply(221, "Closing connection due to server error.");
528 	exit(0);
529 }
530 
531 reply(n, s, args)
532 	int n;
533 	char *s;
534 {
535 
536 	printf("%d ", n);
537 	_doprnt(s, &args, stdout);
538 	printf("\r\n");
539 	fflush(stdout);
540 	if (debug) {
541 		fprintf(stderr, "<--- %d ", n);
542 		_doprnt(s, &args, stderr);
543 		fprintf(stderr, "\n");
544 		fflush(stderr);
545 	}
546 }
547 
548 lreply(n, s, args)
549 	int n;
550 	char *s;
551 {
552 	printf("%d-", n);
553 	_doprnt(s, &args, stdout);
554 	printf("\r\n");
555 	fflush(stdout);
556 	if (debug) {
557 		fprintf(stderr, "<--- %d-", n);
558 		_doprnt(s, &args, stderr);
559 		fprintf(stderr, "\n");
560 	}
561 }
562 
563 replystr(s)
564 	char *s;
565 {
566 	printf("%s\r\n", s);
567 	fflush(stdout);
568 	if (debug)
569 		fprintf(stderr, "<--- %s\n", s);
570 }
571 
572 ack(s)
573 	char *s;
574 {
575 	reply(200, "%s command okay.", s);
576 }
577 
578 nack(s)
579 	char *s;
580 {
581 	reply(502, "%s command not implemented.", s);
582 }
583 
584 yyerror()
585 {
586 	reply(500, "Command not understood.");
587 }
588 
589 delete(name)
590 	char *name;
591 {
592 	struct stat st;
593 
594 	if (stat(name, &st) < 0) {
595 		reply(550, "%s: %s.", name, sys_errlist[errno]);
596 		return;
597 	}
598 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
599 		if (rmdir(name) < 0) {
600 			reply(550, "%s: %s.", name, sys_errlist[errno]);
601 			return;
602 		}
603 		goto done;
604 	}
605 	if (unlink(name) < 0) {
606 		reply(550, "%s: %s.", name, sys_errlist[errno]);
607 		return;
608 	}
609 done:
610 	ack("DELE");
611 }
612 
613 cwd(path)
614 	char *path;
615 {
616 
617 	if (chdir(path) < 0) {
618 		reply(550, "%s: %s.", path, sys_errlist[errno]);
619 		return;
620 	}
621 	ack("CWD");
622 }
623 
624 makedir(name)
625 	char *name;
626 {
627 	struct stat st;
628 	int dochown = stat(name, &st) < 0;
629 
630 	if (mkdir(name, 0777) < 0) {
631 		reply(550, "%s: %s.", name, sys_errlist[errno]);
632 		return;
633 	}
634 	if (dochown)
635 		(void) chown(name, pw->pw_uid, -1);
636 	ack("MKDIR");
637 }
638 
639 removedir(name)
640 	char *name;
641 {
642 
643 	if (rmdir(name) < 0) {
644 		reply(550, "%s: %s.", name, sys_errlist[errno]);
645 		return;
646 	}
647 	ack("RMDIR");
648 }
649 
650 pwd()
651 {
652 	char path[MAXPATHLEN + 1];
653 
654 	if (getwd(path) == NULL) {
655 		reply(451, "%s.", path);
656 		return;
657 	}
658 	reply(251, "\"%s\" is current directory.", path);
659 }
660 
661 char *
662 renamefrom(name)
663 	char *name;
664 {
665 	struct stat st;
666 
667 	if (stat(name, &st) < 0) {
668 		reply(550, "%s: %s.", name, sys_errlist[errno]);
669 		return ((char *)0);
670 	}
671 	reply(350, "File exists, ready for destination name");
672 	return (name);
673 }
674 
675 renamecmd(from, to)
676 	char *from, *to;
677 {
678 
679 	if (rename(from, to) < 0) {
680 		reply(550, "rename: %s.", sys_errlist[errno]);
681 		return;
682 	}
683 	ack("RNTO");
684 }
685 
686 /*
687  * Convert network-format internet address
688  * to base 256 d.d.d.d representation.
689  */
690 char *
691 ntoa(in)
692 	struct in_addr in;
693 {
694 	static char b[18];
695 	register char *p;
696 
697 	p = (char *)&in;
698 #define	UC(b)	(((int)b)&0xff)
699 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
700 	return (b);
701 }
702 
703 dolog(sin)
704 	struct sockaddr_in *sin;
705 {
706 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
707 		sizeof (struct in_addr), AF_INET);
708 	char *remotehost;
709 	time_t t;
710 
711 	if (hp)
712 		remotehost = hp->h_name;
713 	else
714 		remotehost = "UNKNOWNHOST";
715 	t = time(0);
716 	fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t));
717 	fflush(stderr);
718 }
719 
720 /*
721  * Special version of popen which avoids
722  * call to shell.  This insures noone may
723  * create a pipe to a hidden program as a side
724  * effect of a list or dir command.
725  */
726 #define	tst(a,b)	(*mode == 'r'? (b) : (a))
727 #define	RDR	0
728 #define	WTR	1
729 static	int popen_pid[5];
730 
731 static char *
732 nextarg(cpp)
733 	char *cpp;
734 {
735 	register char *cp = cpp;
736 
737 	if (cp == 0)
738 		return (cp);
739 	while (*cp && *cp != ' ' && *cp != '\t')
740 		cp++;
741 	if (*cp == ' ' || *cp == '\t') {
742 		*cp++ = '\0';
743 		while (*cp == ' ' || *cp == '\t')
744 			cp++;
745 	}
746 	if (cp == cpp)
747 		return ((char *)0);
748 	return (cp);
749 }
750 
751 FILE *
752 popen(cmd, mode)
753 	char *cmd, *mode;
754 {
755 	int p[2], ac;
756 	register myside, hisside, pid;
757 	char *av[10];
758 	register char *cp;
759 
760 	if (pipe(p) < 0)
761 		return (NULL);
762 	cp = cmd, ac = 0;
763 	do {
764 		av[ac++] = cp;
765 		cp = nextarg(cp);
766 	} while (cp && *cp);
767 	av[ac] = (char *)0;
768 	myside = tst(p[WTR], p[RDR]);
769 	hisside = tst(p[RDR], p[WTR]);
770 	if ((pid = fork()) == 0) {
771 		/* myside and hisside reverse roles in child */
772 		close(myside);
773 		dup2(hisside, tst(0, 1));
774 		close(hisside);
775 		execv(av[0], av);
776 		_exit(1);
777 	}
778 	if (pid == -1)
779 		return (NULL);
780 	popen_pid[myside] = pid;
781 	close(hisside);
782 	return (fdopen(myside, mode));
783 }
784 
785 pclose(ptr)
786 	FILE *ptr;
787 {
788 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
789 	int status;
790 
791 	f = fileno(ptr);
792 	fclose(ptr);
793 	istat = signal(SIGINT, SIG_IGN);
794 	qstat = signal(SIGQUIT, SIG_IGN);
795 	hstat = signal(SIGHUP, SIG_IGN);
796 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
797 		;
798 	if (r == -1)
799 		status = -1;
800 	signal(SIGINT, istat);
801 	signal(SIGQUIT, qstat);
802 	signal(SIGHUP, hstat);
803 	return (status);
804 }
805 
806 /*
807  * Check user requesting login priviledges.
808  * Disallow anyone mentioned in the file FTPUSERS
809  * to allow people such as uucp to be avoided.
810  */
811 checkuser(name)
812 	register char *name;
813 {
814 	char line[BUFSIZ], *index();
815 	FILE *fd;
816 	int found = 0;
817 
818 	fd = fopen(FTPUSERS, "r");
819 	if (fd == NULL)
820 		return (1);
821 	while (fgets(line, sizeof (line), fd) != NULL) {
822 		register char *cp = index(line, '\n');
823 
824 		if (cp)
825 			*cp = '\0';
826 		if (strcmp(line, name) == 0) {
827 			found++;
828 			break;
829 		}
830 	}
831 	fclose(fd);
832 	return (!found);
833 }
834