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