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