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