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