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