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