xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 27106)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1985 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)ftpd.c	5.5 (Berkeley) 04/16/86";
15 #endif not lint
16 
17 /*
18  * FTP server.
19  */
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <sys/ioctl.h>
23 #include <sys/socket.h>
24 #include <sys/file.h>
25 #include <sys/wait.h>
26 
27 #include <netinet/in.h>
28 
29 #include <arpa/ftp.h>
30 #include <arpa/inet.h>
31 #include <arpa/telnet.h>
32 
33 #include <stdio.h>
34 #include <signal.h>
35 #include <pwd.h>
36 #include <setjmp.h>
37 #include <netdb.h>
38 #include <errno.h>
39 #include <strings.h>
40 #include <syslog.h>
41 
42 /*
43  * File containing login names
44  * NOT to be used on this machine.
45  * Commonly used to disallow uucp.
46  */
47 #define	FTPUSERS	"/etc/ftpusers"
48 
49 extern	int errno;
50 extern	char *sys_errlist[];
51 extern	char *crypt();
52 extern	char version[];
53 extern	char *home;		/* pointer to home directory for glob */
54 extern	FILE *popen(), *fopen(), *freopen();
55 extern	int  pclose(), fclose();
56 extern	char *getline();
57 extern	char cbuf[];
58 
59 struct	sockaddr_in ctrl_addr;
60 struct	sockaddr_in data_source;
61 struct	sockaddr_in data_dest;
62 struct	sockaddr_in his_addr;
63 
64 int	data;
65 jmp_buf	errcatch, urgcatch;
66 int	logged_in;
67 struct	passwd *pw;
68 int	debug;
69 int	timeout = 900;    /* timeout after 15 minutes of inactivity */
70 int	logging;
71 int	guest;
72 int	wtmp;
73 int	type;
74 int	form;
75 int	stru;			/* avoid C keyword */
76 int	mode;
77 int	usedefault = 1;		/* for data transfers */
78 int	pdata;			/* for passive mode */
79 int	unique;
80 int	transflag;
81 char	tmpline[7];
82 char	hostname[32];
83 char	remotehost[32];
84 
85 /*
86  * Timeout intervals for retrying connections
87  * to hosts that don't accept PORT cmds.  This
88  * is a kludge, but given the problems with TCP...
89  */
90 #define	SWAITMAX	90	/* wait at most 90 seconds */
91 #define	SWAITINT	5	/* interval between retries */
92 
93 int	swaitmax = SWAITMAX;
94 int	swaitint = SWAITINT;
95 
96 int	lostconn();
97 int	myoob();
98 FILE	*getdatasock(), *dataconn();
99 
100 main(argc, argv)
101 	int argc;
102 	char *argv[];
103 {
104 	int addrlen;
105 	long pgid;
106 	char *cp;
107 
108 	addrlen = sizeof (his_addr);
109 	if (getpeername(0, &his_addr, &addrlen) < 0) {
110 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
111 		exit(1);
112 	}
113 	addrlen = sizeof (ctrl_addr);
114 	if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) {
115 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
116 		exit(1);
117 	}
118 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
119 	debug = 0;
120 	openlog("ftpd", LOG_PID, LOG_DAEMON);
121 	argc--, argv++;
122 	while (argc > 0 && *argv[0] == '-') {
123 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
124 
125 		case 'v':
126 			debug = 1;
127 			break;
128 
129 		case 'd':
130 			debug = 1;
131 			break;
132 
133 		case 'l':
134 			logging = 1;
135 			break;
136 
137 		case 't':
138 			timeout = atoi(++cp);
139 			goto nextopt;
140 			break;
141 
142 		default:
143 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
144 			     *cp);
145 			break;
146 		}
147 nextopt:
148 		argc--, argv++;
149 	}
150 	(void) signal(SIGPIPE, lostconn);
151 	(void) signal(SIGCHLD, SIG_IGN);
152 	if (signal(SIGURG, myoob) < 0) {
153 		syslog(LOG_ERR, "signal: %m");
154 	}
155 	pgid = getpid();
156 	if (ioctl(fileno(stdin), (int) SIOCSPGRP, (char *) &pgid) < 0) {
157 		syslog(LOG_ERR, "ioctl: %m");
158 	}
159 	dolog(&his_addr);
160 	/* do telnet option negotiation here */
161 	/*
162 	 * Set up default state
163 	 */
164 	logged_in = 0;
165 	data = -1;
166 	type = TYPE_A;
167 	form = FORM_N;
168 	stru = STRU_F;
169 	mode = MODE_S;
170 	tmpline[0] = '\0';
171 	(void) gethostname(hostname, sizeof (hostname));
172 	reply(220, "%s FTP server (%s) ready.",
173 		hostname, version);
174 	for (;;) {
175 		(void) setjmp(errcatch);
176 		(void) yyparse();
177 	}
178 }
179 
180 lostconn()
181 {
182 
183 	if (debug)
184 		syslog(LOG_DEBUG, "lost connection");
185 	dologout(-1);
186 }
187 
188 pass(passwd)
189 	char *passwd;
190 {
191 	char *xpasswd, *savestr();
192 	static struct passwd save;
193 
194 	if (logged_in || pw == NULL) {
195 		reply(503, "Login with USER first.");
196 		return;
197 	}
198 	if (!guest) {		/* "ftp" is only account allowed no password */
199 		xpasswd = crypt(passwd, pw->pw_passwd);
200 		/* The strcmp does not catch null passwords! */
201 		if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
202 			reply(530, "Login incorrect.");
203 			pw = NULL;
204 			return;
205 		}
206 	}
207 	setegid(pw->pw_gid);
208 	initgroups(pw->pw_name, pw->pw_gid);
209 	if (chdir(pw->pw_dir)) {
210 		reply(530, "User %s: can't change directory to %s.",
211 			pw->pw_name, pw->pw_dir);
212 		goto bad;
213 	}
214 
215 	/* grab wtmp before chroot */
216 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
217 	if (guest && chroot(pw->pw_dir) < 0) {
218 		reply(550, "Can't set guest privileges.");
219 		if (wtmp >= 0) {
220 			(void) close(wtmp);
221 			wtmp = -1;
222 		}
223 		goto bad;
224 	}
225 	if (!guest)
226 		reply(230, "User %s logged in.", pw->pw_name);
227 	else
228 		reply(230, "Guest login ok, access restrictions apply.");
229 	logged_in = 1;
230 	dologin(pw);
231 	seteuid(pw->pw_uid);
232 	/*
233 	 * Save everything so globbing doesn't
234 	 * clobber the fields.
235 	 */
236 	save = *pw;
237 	save.pw_name = savestr(pw->pw_name);
238 	save.pw_passwd = savestr(pw->pw_passwd);
239 	save.pw_comment = savestr(pw->pw_comment);
240 	save.pw_gecos = savestr(pw->pw_gecos);
241 	save.pw_dir = savestr(pw->pw_dir);
242 	save.pw_shell = savestr(pw->pw_shell);
243 	pw = &save;
244 	home = pw->pw_dir;		/* home dir for globbing */
245 	return;
246 bad:
247 	seteuid(0);
248 	pw = NULL;
249 }
250 
251 char *
252 savestr(s)
253 	char *s;
254 {
255 	char *malloc();
256 	char *new = malloc((unsigned) strlen(s) + 1);
257 
258 	if (new != NULL)
259 		(void) strcpy(new, s);
260 	return (new);
261 }
262 
263 retrieve(cmd, name)
264 	char *cmd, *name;
265 {
266 	FILE *fin, *dout;
267 	struct stat st;
268 	int (*closefunc)(), tmp;
269 
270 	if (cmd == 0) {
271 #ifdef notdef
272 		/* no remote command execution -- it's a security hole */
273 		if (*name == '|')
274 			fin = popen(name + 1, "r"), closefunc = pclose;
275 		else
276 #endif
277 			fin = fopen(name, "r"), closefunc = fclose;
278 	} else {
279 		char line[BUFSIZ];
280 
281 		(void) sprintf(line, cmd, name), name = line;
282 		fin = popen(line, "r"), closefunc = pclose;
283 	}
284 	if (fin == NULL) {
285 		if (errno != 0)
286 			reply(550, "%s: %s.", name, sys_errlist[errno]);
287 		return;
288 	}
289 	st.st_size = 0;
290 	if (cmd == 0 &&
291 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
292 		reply(550, "%s: not a plain file.", name);
293 		goto done;
294 	}
295 	dout = dataconn(name, st.st_size, "w");
296 	if (dout == NULL)
297 		goto done;
298 	if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
299 		reply(550, "%s: %s.", name, sys_errlist[errno]);
300 	}
301 	else if (tmp == 0) {
302 		reply(226, "Transfer complete.");
303 	}
304 	(void) fclose(dout);
305 	data = -1;
306 	pdata = -1;
307 done:
308 	(*closefunc)(fin);
309 }
310 
311 store(name, mode)
312 	char *name, *mode;
313 {
314 	FILE *fout, *din;
315 	int (*closefunc)(), dochown = 0, tmp;
316 	char *gunique(), *local;
317 
318 #ifdef notdef
319 	/* no remote command execution -- it's a security hole */
320 	if (name[0] == '|')
321 		fout = popen(&name[1], "w"), closefunc = pclose;
322 	else
323 #endif
324 	{
325 		struct stat st;
326 
327 		local = name;
328 		if (stat(name, &st) < 0) {
329 			dochown++;
330 		}
331 		else if (unique) {
332 			if ((local = gunique(name)) == NULL) {
333 				return;
334 			}
335 			dochown++;
336 		}
337 		fout = fopen(local, mode), closefunc = fclose;
338 	}
339 	if (fout == NULL) {
340 		reply(553, "%s: %s.", local, sys_errlist[errno]);
341 		return;
342 	}
343 	din = dataconn(local, (off_t)-1, "r");
344 	if (din == NULL)
345 		goto done;
346 	if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {
347 		reply(552, "%s: %s.", local, sys_errlist[errno]);
348 	}
349 	else if (tmp == 0 && !unique) {
350 		reply(226, "Transfer complete.");
351 	}
352 	else if (tmp == 0 && unique) {
353 		reply(226, "Transfer complete (unique file name:%s).", local);
354 	}
355 	(void) fclose(din);
356 	data = -1;
357 	pdata = -1;
358 done:
359 	if (dochown)
360 		(void) chown(local, pw->pw_uid, -1);
361 	(*closefunc)(fout);
362 }
363 
364 FILE *
365 getdatasock(mode)
366 	char *mode;
367 {
368 	int s, on = 1;
369 
370 	if (data >= 0)
371 		return (fdopen(data, mode));
372 	s = socket(AF_INET, SOCK_STREAM, 0);
373 	if (s < 0)
374 		return (NULL);
375 	seteuid(0);
376 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
377 		goto bad;
378 	/* anchor socket to avoid multi-homing problems */
379 	data_source.sin_family = AF_INET;
380 	data_source.sin_addr = ctrl_addr.sin_addr;
381 	if (bind(s, &data_source, sizeof (data_source)) < 0)
382 		goto bad;
383 	seteuid(pw->pw_uid);
384 	return (fdopen(s, mode));
385 bad:
386 	seteuid(pw->pw_uid);
387 	(void) close(s);
388 	return (NULL);
389 }
390 
391 FILE *
392 dataconn(name, size, mode)
393 	char *name;
394 	off_t size;
395 	char *mode;
396 {
397 	char sizebuf[32];
398 	FILE *file;
399 	int retry = 0;
400 
401 	if (size >= 0)
402 		(void) sprintf (sizebuf, " (%ld bytes)", size);
403 	else
404 		(void) strcpy(sizebuf, "");
405 	if (pdata > 0) {
406 		struct sockaddr_in from;
407 		int s, fromlen = sizeof(from);
408 
409 		s = accept(pdata, &from, &fromlen);
410 		if (s < 0) {
411 			reply(425, "Can't open data connection.");
412 			(void) close(pdata);
413 			pdata = -1;
414 			return(NULL);
415 		}
416 		(void) close(pdata);
417 		pdata = s;
418 		reply(150, "Openning data connection for %s (%s,%d)%s.",
419 		     name, inet_ntoa(from.sin_addr),
420 		     ntohs(from.sin_port), sizebuf);
421 		return(fdopen(pdata, mode));
422 	}
423 	if (data >= 0) {
424 		reply(125, "Using existing data connection for %s%s.",
425 		    name, sizebuf);
426 		usedefault = 1;
427 		return (fdopen(data, mode));
428 	}
429 	if (usedefault)
430 		data_dest = his_addr;
431 	usedefault = 1;
432 	file = getdatasock(mode);
433 	if (file == NULL) {
434 		reply(425, "Can't create data socket (%s,%d): %s.",
435 		    inet_ntoa(data_source.sin_addr),
436 		    ntohs(data_source.sin_port),
437 		    sys_errlist[errno]);
438 		return (NULL);
439 	}
440 	reply(150, "Opening data connection for %s (%s,%d)%s.",
441 	    name, inet_ntoa(data_dest.sin_addr),
442 	    ntohs(data_dest.sin_port), sizebuf);
443 	data = fileno(file);
444 	while (connect(data, &data_dest, sizeof (data_dest)) < 0) {
445 		if (errno == EADDRINUSE && retry < swaitmax) {
446 			sleep((unsigned) swaitint);
447 			retry += swaitint;
448 			continue;
449 		}
450 		reply(425, "Can't build data connection: %s.",
451 		    sys_errlist[errno]);
452 		(void) fclose(file);
453 		data = -1;
454 		return (NULL);
455 	}
456 	return (file);
457 }
458 
459 /*
460  * Tranfer the contents of "instr" to
461  * "outstr" peer using the appropriate
462  * encapulation of the date subject
463  * to Mode, Structure, and Type.
464  *
465  * NB: Form isn't handled.
466  */
467 send_data(instr, outstr)
468 	FILE *instr, *outstr;
469 {
470 	register int c;
471 	int netfd, filefd, cnt;
472 	char buf[BUFSIZ];
473 
474 	transflag++;
475 	if (setjmp(urgcatch)) {
476 		transflag = 0;
477 		return(-1);
478 	}
479 	switch (type) {
480 
481 	case TYPE_A:
482 		while ((c = getc(instr)) != EOF) {
483 			if (c == '\n') {
484 				if (ferror (outstr)) {
485 					transflag = 0;
486 					return (1);
487 				}
488 				putc('\r', outstr);
489 			}
490 			putc(c, outstr);
491 		/*	if (c == '\r')			*/
492 		/*		putc ('\0', outstr);	*/
493 		}
494 		transflag = 0;
495 		if (ferror (instr) || ferror (outstr)) {
496 			return (1);
497 		}
498 		return (0);
499 
500 	case TYPE_I:
501 	case TYPE_L:
502 		netfd = fileno(outstr);
503 		filefd = fileno(instr);
504 
505 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
506 			if (write(netfd, buf, cnt) < 0) {
507 				transflag = 0;
508 				return (1);
509 			}
510 		}
511 		transflag = 0;
512 		return (cnt < 0);
513 	}
514 	reply(550, "Unimplemented TYPE %d in send_data", type);
515 	transflag = 0;
516 	return (-1);
517 }
518 
519 /*
520  * Transfer data from peer to
521  * "outstr" using the appropriate
522  * encapulation of the data subject
523  * to Mode, Structure, and Type.
524  *
525  * N.B.: Form isn't handled.
526  */
527 receive_data(instr, outstr)
528 	FILE *instr, *outstr;
529 {
530 	register int c;
531 	int cnt;
532 	char buf[BUFSIZ];
533 
534 
535 	transflag++;
536 	if (setjmp(urgcatch)) {
537 		transflag = 0;
538 		return(-1);
539 	}
540 	switch (type) {
541 
542 	case TYPE_I:
543 	case TYPE_L:
544 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
545 			if (write(fileno(outstr), buf, cnt) < 0) {
546 				transflag = 0;
547 				return (1);
548 			}
549 		}
550 		transflag = 0;
551 		return (cnt < 0);
552 
553 	case TYPE_E:
554 		reply(553, "TYPE E not implemented.");
555 		transflag = 0;
556 		return (-1);
557 
558 	case TYPE_A:
559 		while ((c = getc(instr)) != EOF) {
560 			if (c == '\r') {
561 				if (ferror (outstr)) {
562 					transflag = 0;
563 					return (1);
564 				}
565 				if ((c = getc(instr)) != '\n')
566 					putc ('\r', outstr);
567 			/*	if (c == '\0')			*/
568 			/*		continue;		*/
569 			}
570 			putc (c, outstr);
571 		}
572 		transflag = 0;
573 		if (ferror (instr) || ferror (outstr))
574 			return (1);
575 		return (0);
576 	}
577 	transflag = 0;
578 	fatal("Unknown type in receive_data.");
579 	/*NOTREACHED*/
580 }
581 
582 fatal(s)
583 	char *s;
584 {
585 	reply(451, "Error in server: %s\n", s);
586 	reply(221, "Closing connection due to server error.");
587 	dologout(0);
588 }
589 
590 /*VARARGS2*/
591 reply(n, s, args)
592 	int n;
593 	char *s;
594 {
595 
596 	printf("%d ", n);
597 	_doprnt(s, &args, stdout);
598 	printf("\r\n");
599 	(void) fflush(stdout);
600 	if (debug) {
601 		syslog(LOG_DEBUG, "<--- %d ", n);
602 		syslog(LOG_DEBUG, s, &args);
603 	}
604 }
605 
606 /*VARARGS2*/
607 lreply(n, s, args)
608 	int n;
609 	char *s;
610 {
611 	printf("%d-", n);
612 	_doprnt(s, &args, stdout);
613 	printf("\r\n");
614 	(void) fflush(stdout);
615 	if (debug) {
616 		syslog(LOG_DEBUG, "<--- %d- ", n);
617 		syslog(LOG_DEBUG, s, &args);
618 	}
619 }
620 
621 ack(s)
622 	char *s;
623 {
624 	reply(250, "%s command successful.", s);
625 }
626 
627 nack(s)
628 	char *s;
629 {
630 	reply(502, "%s command not implemented.", s);
631 }
632 
633 yyerror(s)
634 	char *s;
635 {
636 	char *cp;
637 
638 	cp = index(cbuf,'\n');
639 	*cp = '\0';
640 	reply(500, "'%s': command not understood.",cbuf);
641 }
642 
643 delete(name)
644 	char *name;
645 {
646 	struct stat st;
647 
648 	if (stat(name, &st) < 0) {
649 		reply(550, "%s: %s.", name, sys_errlist[errno]);
650 		return;
651 	}
652 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
653 		if (rmdir(name) < 0) {
654 			reply(550, "%s: %s.", name, sys_errlist[errno]);
655 			return;
656 		}
657 		goto done;
658 	}
659 	if (unlink(name) < 0) {
660 		reply(550, "%s: %s.", name, sys_errlist[errno]);
661 		return;
662 	}
663 done:
664 	ack("DELE");
665 }
666 
667 cwd(path)
668 	char *path;
669 {
670 
671 	if (chdir(path) < 0) {
672 		reply(550, "%s: %s.", path, sys_errlist[errno]);
673 		return;
674 	}
675 	ack("CWD");
676 }
677 
678 makedir(name)
679 	char *name;
680 {
681 	struct stat st;
682 	int dochown = stat(name, &st) < 0;
683 
684 	if (mkdir(name, 0777) < 0) {
685 		reply(550, "%s: %s.", name, sys_errlist[errno]);
686 		return;
687 	}
688 	if (dochown)
689 		(void) chown(name, pw->pw_uid, -1);
690 	reply(257, "MKD command successful.");
691 }
692 
693 removedir(name)
694 	char *name;
695 {
696 
697 	if (rmdir(name) < 0) {
698 		reply(550, "%s: %s.", name, sys_errlist[errno]);
699 		return;
700 	}
701 	ack("RMD");
702 }
703 
704 pwd()
705 {
706 	char path[MAXPATHLEN + 1];
707 
708 	if (getwd(path) == NULL) {
709 		reply(550, "%s.", path);
710 		return;
711 	}
712 	reply(257, "\"%s\" is current directory.", path);
713 }
714 
715 char *
716 renamefrom(name)
717 	char *name;
718 {
719 	struct stat st;
720 
721 	if (stat(name, &st) < 0) {
722 		reply(550, "%s: %s.", name, sys_errlist[errno]);
723 		return ((char *)0);
724 	}
725 	reply(350, "File exists, ready for destination name");
726 	return (name);
727 }
728 
729 renamecmd(from, to)
730 	char *from, *to;
731 {
732 
733 	if (rename(from, to) < 0) {
734 		reply(550, "rename: %s.", sys_errlist[errno]);
735 		return;
736 	}
737 	ack("RNTO");
738 }
739 
740 dolog(sin)
741 	struct sockaddr_in *sin;
742 {
743 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
744 		sizeof (struct in_addr), AF_INET);
745 	time_t t;
746 	extern char *ctime();
747 
748 	if (hp) {
749 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
750 		endhostent();
751 	} else
752 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
753 		    sizeof (remotehost));
754 	if (!logging)
755 		return;
756 	t = time((time_t *) 0);
757 	syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t));
758 }
759 
760 #include <utmp.h>
761 
762 #define	SCPYN(a, b)	(void) strncpy(a, b, sizeof (a))
763 struct	utmp utmp;
764 
765 /*
766  * Record login in wtmp file.
767  */
768 dologin(pw)
769 	struct passwd *pw;
770 {
771 	char line[32];
772 
773 	if (wtmp >= 0) {
774 		/* hack, but must be unique and no tty line */
775 		(void) sprintf(line, "ftp%d", getpid());
776 		SCPYN(utmp.ut_line, line);
777 		SCPYN(utmp.ut_name, pw->pw_name);
778 		SCPYN(utmp.ut_host, remotehost);
779 		utmp.ut_time = (long) time((time_t *) 0);
780 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
781 		if (!guest) {		/* anon must hang on */
782 			(void) close(wtmp);
783 			wtmp = -1;
784 		}
785 	}
786 }
787 
788 /*
789  * Record logout in wtmp file
790  * and exit with supplied status.
791  */
792 dologout(status)
793 	int status;
794 {
795 
796 	if (logged_in) {
797 		(void) seteuid(0);
798 		if (wtmp < 0)
799 			wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
800 		if (wtmp >= 0) {
801 			SCPYN(utmp.ut_name, "");
802 			SCPYN(utmp.ut_host, "");
803 			utmp.ut_time = (long) time((time_t *) 0);
804 			(void) write(wtmp, (char *)&utmp, sizeof (utmp));
805 			(void) close(wtmp);
806 		}
807 	}
808 	/* beware of flushing buffers after a SIGPIPE */
809 	_exit(status);
810 }
811 
812 /*
813  * Special version of popen which avoids
814  * call to shell.  This insures noone may
815  * create a pipe to a hidden program as a side
816  * effect of a list or dir command.
817  */
818 #define	tst(a,b)	(*mode == 'r'? (b) : (a))
819 #define	RDR	0
820 #define	WTR	1
821 static	int popen_pid[5];
822 
823 static char *
824 nextarg(cpp)
825 	char *cpp;
826 {
827 	register char *cp = cpp;
828 
829 	if (cp == 0)
830 		return (cp);
831 	while (*cp && *cp != ' ' && *cp != '\t')
832 		cp++;
833 	if (*cp == ' ' || *cp == '\t') {
834 		*cp++ = '\0';
835 		while (*cp == ' ' || *cp == '\t')
836 			cp++;
837 	}
838 	if (cp == cpp)
839 		return ((char *)0);
840 	return (cp);
841 }
842 
843 FILE *
844 popen(cmd, mode)
845 	char *cmd, *mode;
846 {
847 	int p[2], ac, gac;
848 	register myside, hisside, pid;
849 	char *av[20], *gav[512];
850 	register char *cp;
851 
852 	if (pipe(p) < 0)
853 		return (NULL);
854 	cp = cmd, ac = 0;
855 	/* break up string into pieces */
856 	do {
857 		av[ac++] = cp;
858 		cp = nextarg(cp);
859 	} while (cp && *cp && ac < 20);
860 	av[ac] = (char *)0;
861 	gav[0] = av[0];
862 	/* glob each piece */
863 	for (gac = ac = 1; av[ac] != NULL; ac++) {
864 		char **pop;
865 		extern char **glob(), **copyblk();
866 
867 		pop = glob(av[ac]);
868 		if (pop == (char **)NULL) {	/* globbing failed */
869 			char *vv[2];
870 
871 			vv[0] = av[ac];
872 			vv[1] = 0;
873 			pop = copyblk(vv);
874 		}
875 		av[ac] = (char *)pop;		/* save to free later */
876 		while (*pop && gac < 512)
877 			gav[gac++] = *pop++;
878 	}
879 	gav[gac] = (char *)0;
880 	myside = tst(p[WTR], p[RDR]);
881 	hisside = tst(p[RDR], p[WTR]);
882 	if ((pid = fork()) == 0) {
883 		/* myside and hisside reverse roles in child */
884 		(void) close(myside);
885 		(void) dup2(hisside, tst(0, 1));
886 		(void) close(hisside);
887 		execv(gav[0], gav);
888 		_exit(1);
889 	}
890 	for (ac = 1; av[ac] != NULL; ac++)
891 		blkfree((char **)av[ac]);
892 	if (pid == -1)
893 		return (NULL);
894 	popen_pid[myside] = pid;
895 	(void) close(hisside);
896 	return (fdopen(myside, mode));
897 }
898 
899 pclose(ptr)
900 	FILE *ptr;
901 {
902 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
903 	int status;
904 
905 	f = fileno(ptr);
906 	(void) fclose(ptr);
907 	istat = signal(SIGINT, SIG_IGN);
908 	qstat = signal(SIGQUIT, SIG_IGN);
909 	hstat = signal(SIGHUP, SIG_IGN);
910 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
911 		;
912 	if (r == -1)
913 		status = -1;
914 	(void) signal(SIGINT, istat);
915 	(void) signal(SIGQUIT, qstat);
916 	(void) signal(SIGHUP, hstat);
917 	return (status);
918 }
919 
920 /*
921  * Check user requesting login priviledges.
922  * Disallow anyone mentioned in the file FTPUSERS
923  * to allow people such as uucp to be avoided.
924  */
925 checkuser(name)
926 	register char *name;
927 {
928 	char line[BUFSIZ], *index();
929 	FILE *fd;
930 	int found = 0;
931 
932 	fd = fopen(FTPUSERS, "r");
933 	if (fd == NULL)
934 		return (1);
935 	while (fgets(line, sizeof (line), fd) != NULL) {
936 		register char *cp = index(line, '\n');
937 
938 		if (cp)
939 			*cp = '\0';
940 		if (strcmp(line, name) == 0) {
941 			found++;
942 			break;
943 		}
944 	}
945 	(void) fclose(fd);
946 	return (!found);
947 }
948 
949 myoob()
950 {
951 	int aflag = 0, atmark;
952 	char c, *cp;
953 
954 	if (!transflag) {
955 		for (;;) {
956 			if (ioctl(fileno(stdin), (int) SIOCATMARK, (char *) &atmark) < 0) {
957 				syslog(LOG_ERR, "ioctl: %m");
958 				break;
959 			}
960 			if (atmark)
961 				break;
962 			(void) read(fileno(stdin), &c, 1);
963 		}
964 		(void) recv(fileno(stdin), &c, 1, MSG_OOB);
965 		(void) read(fileno(stdin), &c, 1);
966 		return;
967 	}
968 	for (;;) {
969 		if (ioctl(fileno(stdin), (int) SIOCATMARK, (char *) &atmark) < 0) {
970 			syslog(LOG_ERR, "ioctl: %m");
971 			break;
972 		}
973 		if (atmark)
974 			break;
975 		(void) read(fileno(stdin), &c, 1);
976 		if (c == IAC || c == IP)
977 			aflag++;
978 	}
979 	(void) recv(fileno(stdin), &c, 1, MSG_OOB);
980 	if (c == IAC)
981 		aflag++;
982 	(void) read(fileno(stdin), &c, 1);
983 	if (c == DM)
984 		aflag++;
985 	if (aflag != 4)
986 		return;
987 	cp = tmpline;
988 	(void) getline(cp, 7, stdin);
989 	upper(cp);
990 	if (strcmp(cp, "ABOR\r\n"))
991 		return;
992 	tmpline[0] = '\0';
993 	reply(426,"Transfer aborted. Data connection closed.");
994 	reply(226,"Abort successful");
995 	longjmp(urgcatch, 1);
996 }
997 
998 /*
999  * Note: The 530 reply codes could be 4xx codes, except nothing is
1000  * given in the state tables except 421 which implies an exit.  (RFC959)
1001  */
1002 passive()
1003 {
1004 	int len;
1005 	struct sockaddr_in tmp;
1006 	register char *p, *a;
1007 
1008 	pdata = socket(AF_INET, SOCK_STREAM, 0);
1009 	if (pdata < 0) {
1010 		reply(530, "Can't open passive connection");
1011 		return;
1012 	}
1013 	tmp = ctrl_addr;
1014 	tmp.sin_port = 0;
1015 	seteuid(0);
1016 	if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
1017 		seteuid(pw->pw_uid);
1018 		(void) close(pdata);
1019 		pdata = -1;
1020 		reply(530, "Can't open passive connection");
1021 		return;
1022 	}
1023 	seteuid(pw->pw_uid);
1024 	len = sizeof(tmp);
1025 	if (getsockname(pdata, (char *) &tmp, &len) < 0) {
1026 		(void) close(pdata);
1027 		pdata = -1;
1028 		reply(530, "Can't open passive connection");
1029 		return;
1030 	}
1031 	if (listen(pdata, 1) < 0) {
1032 		(void) close(pdata);
1033 		pdata = -1;
1034 		reply(530, "Can't open passive connection");
1035 		return;
1036 	}
1037 	a = (char *) &tmp.sin_addr;
1038 	p = (char *) &tmp.sin_port;
1039 
1040 #define UC(b) (((int) b) & 0xff)
1041 
1042 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1043 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1044 }
1045 
1046 char *
1047 gunique(local)
1048 	char *local;
1049 {
1050 	static char new[MAXPATHLEN];
1051 	char *cp = rindex(local, '/');
1052 	int d, count=0;
1053 	char ext = '1';
1054 
1055 	if (cp) {
1056 		*cp = '\0';
1057 	}
1058 	d = access(cp ? local : ".", 2);
1059 	if (cp) {
1060 		*cp = '/';
1061 	}
1062 	if (d < 0) {
1063 		syslog(LOG_ERR, "%s: %m", local);
1064 		return((char *) 0);
1065 	}
1066 	(void) strcpy(new, local);
1067 	cp = new + strlen(new);
1068 	*cp++ = '.';
1069 	while (!d) {
1070 		if (++count == 100) {
1071 			reply(452, "Unique file name not cannot be created.");
1072 			return((char *) 0);
1073 		}
1074 		*cp++ = ext;
1075 		*cp = '\0';
1076 		if (ext == '9') {
1077 			ext = '0';
1078 		}
1079 		else {
1080 			ext++;
1081 		}
1082 		if ((d = access(new, 0)) < 0) {
1083 			break;
1084 		}
1085 		if (ext != '0') {
1086 			cp--;
1087 		}
1088 		else if (*(cp - 2) == '.') {
1089 			*(cp - 1) = '1';
1090 		}
1091 		else {
1092 			*(cp - 2) = *(cp - 2) + 1;
1093 			cp--;
1094 		}
1095 	}
1096 	return(new);
1097 }
1098