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