386BSD 0.1 development
[unix-history] / .ref-386BSD-0.0 / usr / src / libexec / ftpd / ftpd.c
CommitLineData
1b3fef4f
WJ
1/*
2 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35char copyright[] =
36"@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
37 All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)ftpd.c 5.40 (Berkeley) 7/2/91";
42#endif /* not lint */
43
44/*
45 * FTP server.
46 */
47#include <sys/param.h>
48#include <sys/stat.h>
49#include <sys/ioctl.h>
50#include <sys/socket.h>
51#include <sys/wait.h>
52
53#include <netinet/in.h>
54#include <netinet/in_systm.h>
55#include <netinet/ip.h>
56
57#define FTP_NAMES
58#include <arpa/ftp.h>
59#include <arpa/inet.h>
60#include <arpa/telnet.h>
61
62#include <signal.h>
63#include <dirent.h>
64#include <fcntl.h>
65#include <time.h>
66#include <pwd.h>
67#include <setjmp.h>
68#include <netdb.h>
69#include <errno.h>
70#include <syslog.h>
71#include <varargs.h>
72#include <unistd.h>
73#include <stdio.h>
74#include <ctype.h>
75#include <stdlib.h>
76#include <string.h>
77#include "pathnames.h"
78
79/*
80 * File containing login names
81 * NOT to be used on this machine.
82 * Commonly used to disallow uucp.
83 */
84extern int errno;
85extern char *crypt();
86extern char version[];
87extern char *home; /* pointer to home directory for glob */
88extern FILE *ftpd_popen(), *fopen(), *freopen();
89extern int ftpd_pclose(), fclose();
90extern char *getline();
91extern char cbuf[];
92extern off_t restart_point;
93
94struct sockaddr_in ctrl_addr;
95struct sockaddr_in data_source;
96struct sockaddr_in data_dest;
97struct sockaddr_in his_addr;
98struct sockaddr_in pasv_addr;
99
100int data;
101jmp_buf errcatch, urgcatch;
102int logged_in;
103struct passwd *pw;
104int debug;
105int timeout = 900; /* timeout after 15 minutes of inactivity */
106int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
107int logging;
108int guest;
109int type;
110int form;
111int stru; /* avoid C keyword */
112int mode;
113int usedefault = 1; /* for data transfers */
114int pdata = -1; /* for passive mode */
115int transflag;
116off_t file_size;
117off_t byte_count;
118#if !defined(CMASK) || CMASK == 0
119#undef CMASK
120#define CMASK 027
121#endif
122int defumask = CMASK; /* default umask value */
123char tmpline[7];
124char hostname[MAXHOSTNAMELEN];
125char remotehost[MAXHOSTNAMELEN];
126
127/*
128 * Timeout intervals for retrying connections
129 * to hosts that don't accept PORT cmds. This
130 * is a kludge, but given the problems with TCP...
131 */
132#define SWAITMAX 90 /* wait at most 90 seconds */
133#define SWAITINT 5 /* interval between retries */
134
135int swaitmax = SWAITMAX;
136int swaitint = SWAITINT;
137
138void lostconn(), myoob();
139FILE *getdatasock(), *dataconn();
140
141#ifdef SETPROCTITLE
142char **Argv = NULL; /* pointer to argument vector */
143char *LastArgv = NULL; /* end of argv */
144char proctitle[BUFSIZ]; /* initial part of title */
145#endif /* SETPROCTITLE */
146
147main(argc, argv, envp)
148 int argc;
149 char *argv[];
150 char **envp;
151{
152 int addrlen, on = 1, tos;
153 char *cp;
154
155 /*
156 * LOG_NDELAY sets up the logging connection immediately,
157 * necessary for anonymous ftp's that chroot and can't do it later.
158 */
159 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
160 addrlen = sizeof (his_addr);
161 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
162 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
163 exit(1);
164 }
165 addrlen = sizeof (ctrl_addr);
166 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
167 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
168 exit(1);
169 }
170#ifdef IP_TOS
171 tos = IPTOS_LOWDELAY;
172 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
173 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
174#endif
175 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
176 debug = 0;
177#ifdef SETPROCTITLE
178 /*
179 * Save start and extent of argv for setproctitle.
180 */
181 Argv = argv;
182 while (*envp)
183 envp++;
184 LastArgv = envp[-1] + strlen(envp[-1]);
185#endif /* SETPROCTITLE */
186
187 argc--, argv++;
188 while (argc > 0 && *argv[0] == '-') {
189 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
190
191 case 'v':
192 debug = 1;
193 break;
194
195 case 'd':
196 debug = 1;
197 break;
198
199 case 'l':
200 logging = 1;
201 break;
202
203 case 't':
204 timeout = atoi(++cp);
205 if (maxtimeout < timeout)
206 maxtimeout = timeout;
207 goto nextopt;
208
209 case 'T':
210 maxtimeout = atoi(++cp);
211 if (timeout > maxtimeout)
212 timeout = maxtimeout;
213 goto nextopt;
214
215 case 'u':
216 {
217 int val = 0;
218
219 while (*++cp && *cp >= '0' && *cp <= '9')
220 val = val*8 + *cp - '0';
221 if (*cp)
222 fprintf(stderr, "ftpd: Bad value for -u\n");
223 else
224 defumask = val;
225 goto nextopt;
226 }
227
228 default:
229 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
230 *cp);
231 break;
232 }
233nextopt:
234 argc--, argv++;
235 }
236 (void) freopen(_PATH_DEVNULL, "w", stderr);
237 (void) signal(SIGPIPE, lostconn);
238 (void) signal(SIGCHLD, SIG_IGN);
239 if ((int)signal(SIGURG, myoob) < 0)
240 syslog(LOG_ERR, "signal: %m");
241
242 /* Try to handle urgent data inline */
243#ifdef SO_OOBINLINE
244 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
245 syslog(LOG_ERR, "setsockopt: %m");
246#endif
247
248#ifdef F_SETOWN
249 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
250 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
251#endif
252 dolog(&his_addr);
253 /*
254 * Set up default state
255 */
256 data = -1;
257 type = TYPE_A;
258 form = FORM_N;
259 stru = STRU_F;
260 mode = MODE_S;
261 tmpline[0] = '\0';
262 (void) gethostname(hostname, sizeof (hostname));
263 reply(220, "%s FTP server (%s) ready.", hostname, version);
264 (void) setjmp(errcatch);
265 for (;;)
266 (void) yyparse();
267 /* NOTREACHED */
268}
269
270void
271lostconn()
272{
273 if (debug)
274 syslog(LOG_DEBUG, "lost connection");
275 dologout(-1);
276}
277
278static char ttyline[20];
279
280/*
281 * Helper function for sgetpwnam().
282 */
283char *
284sgetsave(s)
285 char *s;
286{
287 char *new = malloc((unsigned) strlen(s) + 1);
288
289 if (new == NULL) {
290 perror_reply(421, "Local resource failure: malloc");
291 dologout(1);
292 /* NOTREACHED */
293 }
294 (void) strcpy(new, s);
295 return (new);
296}
297
298/*
299 * Save the result of a getpwnam. Used for USER command, since
300 * the data returned must not be clobbered by any other command
301 * (e.g., globbing).
302 */
303struct passwd *
304sgetpwnam(name)
305 char *name;
306{
307 static struct passwd save;
308 register struct passwd *p;
309 char *sgetsave();
310
311 if ((p = getpwnam(name)) == NULL)
312 return (p);
313 if (save.pw_name) {
314 free(save.pw_name);
315 free(save.pw_passwd);
316 free(save.pw_gecos);
317 free(save.pw_dir);
318 free(save.pw_shell);
319 }
320 save = *p;
321 save.pw_name = sgetsave(p->pw_name);
322 save.pw_passwd = sgetsave(p->pw_passwd);
323 save.pw_gecos = sgetsave(p->pw_gecos);
324 save.pw_dir = sgetsave(p->pw_dir);
325 save.pw_shell = sgetsave(p->pw_shell);
326 return (&save);
327}
328
329int login_attempts; /* number of failed login attempts */
330int askpasswd; /* had user command, ask for passwd */
331
332/*
333 * USER command.
334 * Sets global passwd pointer pw if named account exists and is acceptable;
335 * sets askpasswd if a PASS command is expected. If logged in previously,
336 * need to reset state. If name is "ftp" or "anonymous", the name is not in
337 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
338 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
339 * requesting login privileges. Disallow anyone who does not have a standard
340 * shell as returned by getusershell(). Disallow anyone mentioned in the file
341 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
342 */
343user(name)
344 char *name;
345{
346 register char *cp;
347 char *shell;
348 char *getusershell();
349
350 if (logged_in) {
351 if (guest) {
352 reply(530, "Can't change user from guest login.");
353 return;
354 }
355 end_login();
356 }
357
358 guest = 0;
359 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
360 if (checkuser("ftp") || checkuser("anonymous"))
361 reply(530, "User %s access denied.", name);
362 else if ((pw = sgetpwnam("ftp")) != NULL) {
363 guest = 1;
364 askpasswd = 1;
365 reply(331, "Guest login ok, send ident as password.");
366 } else
367 reply(530, "User %s unknown.", name);
368 return;
369 }
370 if (pw = sgetpwnam(name)) {
371 if ((shell = pw->pw_shell) == NULL || *shell == 0)
372 shell = _PATH_BSHELL;
373 while ((cp = getusershell()) != NULL)
374 if (strcmp(cp, shell) == 0)
375 break;
376 endusershell();
377 if (cp == NULL || checkuser(name)) {
378 reply(530, "User %s access denied.", name);
379 if (logging)
380 syslog(LOG_NOTICE,
381 "FTP LOGIN REFUSED FROM %s, %s",
382 remotehost, name);
383 pw = (struct passwd *) NULL;
384 return;
385 }
386 }
387 reply(331, "Password required for %s.", name);
388 askpasswd = 1;
389 /*
390 * Delay before reading passwd after first failed
391 * attempt to slow down passwd-guessing programs.
392 */
393 if (login_attempts)
394 sleep((unsigned) login_attempts);
395}
396
397/*
398 * Check if a user is in the file _PATH_FTPUSERS
399 */
400checkuser(name)
401 char *name;
402{
403 register FILE *fd;
404 register char *p;
405 char line[BUFSIZ];
406
407 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
408 while (fgets(line, sizeof(line), fd) != NULL)
409 if ((p = index(line, '\n')) != NULL) {
410 *p = '\0';
411 if (line[0] == '#')
412 continue;
413 if (strcmp(line, name) == 0)
414 return (1);
415 }
416 (void) fclose(fd);
417 }
418 return (0);
419}
420
421/*
422 * Terminate login as previous user, if any, resetting state;
423 * used when USER command is given or login fails.
424 */
425end_login()
426{
427
428 (void) seteuid((uid_t)0);
429 if (logged_in)
430 logwtmp(ttyline, "", "");
431 pw = NULL;
432 logged_in = 0;
433 guest = 0;
434}
435
436pass(passwd)
437 char *passwd;
438{
439 char *xpasswd, *salt;
440
441 if (logged_in || askpasswd == 0) {
442 reply(503, "Login with USER first.");
443 return;
444 }
445 askpasswd = 0;
446 if (!guest) { /* "ftp" is only account allowed no password */
447 if (pw == NULL)
448 salt = "xx";
449 else
450 salt = pw->pw_passwd;
451#ifdef DES
452 xpasswd = crypt(passwd, salt);
453#else
454 xpasswd = passwd;
455#endif
456 /* The strcmp does not catch null passwords! */
457 if (pw == NULL || *pw->pw_passwd == '\0' ||
458 strcmp(xpasswd, pw->pw_passwd)) {
459 reply(530, "Login incorrect.");
460 pw = NULL;
461 if (login_attempts++ >= 5) {
462 syslog(LOG_NOTICE,
463 "repeated login failures from %s",
464 remotehost);
465 exit(0);
466 }
467 return;
468 }
469 }
470 login_attempts = 0; /* this time successful */
471 (void) setegid((gid_t)pw->pw_gid);
472 (void) initgroups(pw->pw_name, pw->pw_gid);
473
474 /* open wtmp before chroot */
475 (void)sprintf(ttyline, "ftp%d", getpid());
476 logwtmp(ttyline, pw->pw_name, remotehost);
477 logged_in = 1;
478
479 if (guest) {
480 /*
481 * We MUST do a chdir() after the chroot. Otherwise
482 * the old current directory will be accessible as "."
483 * outside the new root!
484 */
485 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
486 reply(550, "Can't set guest privileges.");
487 goto bad;
488 }
489 } else if (chdir(pw->pw_dir) < 0) {
490 if (chdir("/") < 0) {
491 reply(530, "User %s: can't change directory to %s.",
492 pw->pw_name, pw->pw_dir);
493 goto bad;
494 } else
495 lreply(230, "No directory! Logging in with home=/");
496 }
497 if (seteuid((uid_t)pw->pw_uid) < 0) {
498 reply(550, "Can't set uid.");
499 goto bad;
500 }
501 if (guest) {
502 reply(230, "Guest login ok, access restrictions apply.");
503#ifdef SETPROCTITLE
504 sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
505 sizeof(proctitle) - sizeof(remotehost) -
506 sizeof(": anonymous/"), passwd);
507 setproctitle(proctitle);
508#endif /* SETPROCTITLE */
509 if (logging)
510 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
511 remotehost, passwd);
512 } else {
513 reply(230, "User %s logged in.", pw->pw_name);
514#ifdef SETPROCTITLE
515 sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
516 setproctitle(proctitle);
517#endif /* SETPROCTITLE */
518 if (logging)
519 syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
520 remotehost, pw->pw_name);
521 }
522 home = pw->pw_dir; /* home dir for globbing */
523 (void) umask(defumask);
524 return;
525bad:
526 /* Forget all about it... */
527 end_login();
528}
529
530retrieve(cmd, name)
531 char *cmd, *name;
532{
533 FILE *fin, *dout;
534 struct stat st;
535 int (*closefunc)();
536
537 if (cmd == 0) {
538 fin = fopen(name, "r"), closefunc = fclose;
539 st.st_size = 0;
540 } else {
541 char line[BUFSIZ];
542
543 (void) sprintf(line, cmd, name), name = line;
544 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
545 st.st_size = -1;
546 st.st_blksize = BUFSIZ;
547 }
548 if (fin == NULL) {
549 if (errno != 0)
550 perror_reply(550, name);
551 return;
552 }
553 if (cmd == 0 &&
554 (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
555 reply(550, "%s: not a plain file.", name);
556 goto done;
557 }
558 if (restart_point) {
559 if (type == TYPE_A) {
560 register int i, n, c;
561
562 n = restart_point;
563 i = 0;
564 while (i++ < n) {
565 if ((c=getc(fin)) == EOF) {
566 perror_reply(550, name);
567 goto done;
568 }
569 if (c == '\n')
570 i++;
571 }
572 } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
573 perror_reply(550, name);
574 goto done;
575 }
576 }
577 dout = dataconn(name, st.st_size, "w");
578 if (dout == NULL)
579 goto done;
580 send_data(fin, dout, st.st_blksize);
581 (void) fclose(dout);
582 data = -1;
583 pdata = -1;
584done:
585 (*closefunc)(fin);
586}
587
588store(name, mode, unique)
589 char *name, *mode;
590 int unique;
591{
592 FILE *fout, *din;
593 struct stat st;
594 int (*closefunc)();
595 char *gunique();
596
597 if (unique && stat(name, &st) == 0 &&
598 (name = gunique(name)) == NULL)
599 return;
600
601 if (restart_point)
602 mode = "r+w";
603 fout = fopen(name, mode);
604 closefunc = fclose;
605 if (fout == NULL) {
606 perror_reply(553, name);
607 return;
608 }
609 if (restart_point) {
610 if (type == TYPE_A) {
611 register int i, n, c;
612
613 n = restart_point;
614 i = 0;
615 while (i++ < n) {
616 if ((c=getc(fout)) == EOF) {
617 perror_reply(550, name);
618 goto done;
619 }
620 if (c == '\n')
621 i++;
622 }
623 /*
624 * We must do this seek to "current" position
625 * because we are changing from reading to
626 * writing.
627 */
628 if (fseek(fout, 0L, L_INCR) < 0) {
629 perror_reply(550, name);
630 goto done;
631 }
632 } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
633 perror_reply(550, name);
634 goto done;
635 }
636 }
637 din = dataconn(name, (off_t)-1, "r");
638 if (din == NULL)
639 goto done;
640 if (receive_data(din, fout) == 0) {
641 if (unique)
642 reply(226, "Transfer complete (unique file name:%s).",
643 name);
644 else
645 reply(226, "Transfer complete.");
646 }
647 (void) fclose(din);
648 data = -1;
649 pdata = -1;
650done:
651 (*closefunc)(fout);
652}
653
654FILE *
655getdatasock(mode)
656 char *mode;
657{
658 int s, on = 1, tries;
659
660 if (data >= 0)
661 return (fdopen(data, mode));
662 (void) seteuid((uid_t)0);
663 s = socket(AF_INET, SOCK_STREAM, 0);
664 if (s < 0)
665 goto bad;
666 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
667 (char *) &on, sizeof (on)) < 0)
668 goto bad;
669 /* anchor socket to avoid multi-homing problems */
670 data_source.sin_family = AF_INET;
671 data_source.sin_addr = ctrl_addr.sin_addr;
672 for (tries = 1; ; tries++) {
673 if (bind(s, (struct sockaddr *)&data_source,
674 sizeof (data_source)) >= 0)
675 break;
676 if (errno != EADDRINUSE || tries > 10)
677 goto bad;
678 sleep(tries);
679 }
680 (void) seteuid((uid_t)pw->pw_uid);
681#ifdef IP_TOS
682 on = IPTOS_THROUGHPUT;
683 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
684 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
685#endif
686 return (fdopen(s, mode));
687bad:
688 (void) seteuid((uid_t)pw->pw_uid);
689 (void) close(s);
690 return (NULL);
691}
692
693FILE *
694dataconn(name, size, mode)
695 char *name;
696 off_t size;
697 char *mode;
698{
699 char sizebuf[32];
700 FILE *file;
701 int retry = 0, tos;
702
703 file_size = size;
704 byte_count = 0;
705 if (size != (off_t) -1)
706 (void) sprintf (sizebuf, " (%ld bytes)", size);
707 else
708 (void) strcpy(sizebuf, "");
709 if (pdata >= 0) {
710 struct sockaddr_in from;
711 int s, fromlen = sizeof(from);
712
713 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
714 if (s < 0) {
715 reply(425, "Can't open data connection.");
716 (void) close(pdata);
717 pdata = -1;
718 return(NULL);
719 }
720 (void) close(pdata);
721 pdata = s;
722#ifdef IP_TOS
723 tos = IPTOS_LOWDELAY;
724 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
725 sizeof(int));
726#endif
727 reply(150, "Opening %s mode data connection for %s%s.",
728 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
729 return(fdopen(pdata, mode));
730 }
731 if (data >= 0) {
732 reply(125, "Using existing data connection for %s%s.",
733 name, sizebuf);
734 usedefault = 1;
735 return (fdopen(data, mode));
736 }
737 if (usedefault)
738 data_dest = his_addr;
739 usedefault = 1;
740 file = getdatasock(mode);
741 if (file == NULL) {
742 reply(425, "Can't create data socket (%s,%d): %s.",
743 inet_ntoa(data_source.sin_addr),
744 ntohs(data_source.sin_port), strerror(errno));
745 return (NULL);
746 }
747 data = fileno(file);
748 while (connect(data, (struct sockaddr *)&data_dest,
749 sizeof (data_dest)) < 0) {
750 if (errno == EADDRINUSE && retry < swaitmax) {
751 sleep((unsigned) swaitint);
752 retry += swaitint;
753 continue;
754 }
755 perror_reply(425, "Can't build data connection");
756 (void) fclose(file);
757 data = -1;
758 return (NULL);
759 }
760 reply(150, "Opening %s mode data connection for %s%s.",
761 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
762 return (file);
763}
764
765/*
766 * Tranfer the contents of "instr" to
767 * "outstr" peer using the appropriate
768 * encapsulation of the data subject
769 * to Mode, Structure, and Type.
770 *
771 * NB: Form isn't handled.
772 */
773send_data(instr, outstr, blksize)
774 FILE *instr, *outstr;
775 off_t blksize;
776{
777 register int c, cnt;
778 register char *buf;
779 int netfd, filefd;
780
781 transflag++;
782 if (setjmp(urgcatch)) {
783 transflag = 0;
784 return;
785 }
786 switch (type) {
787
788 case TYPE_A:
789 while ((c = getc(instr)) != EOF) {
790 byte_count++;
791 if (c == '\n') {
792 if (ferror(outstr))
793 goto data_err;
794 (void) putc('\r', outstr);
795 }
796 (void) putc(c, outstr);
797 }
798 fflush(outstr);
799 transflag = 0;
800 if (ferror(instr))
801 goto file_err;
802 if (ferror(outstr))
803 goto data_err;
804 reply(226, "Transfer complete.");
805 return;
806
807 case TYPE_I:
808 case TYPE_L:
809 if ((buf = malloc((u_int)blksize)) == NULL) {
810 transflag = 0;
811 perror_reply(451, "Local resource failure: malloc");
812 return;
813 }
814 netfd = fileno(outstr);
815 filefd = fileno(instr);
816 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
817 write(netfd, buf, cnt) == cnt)
818 byte_count += cnt;
819 transflag = 0;
820 (void)free(buf);
821 if (cnt != 0) {
822 if (cnt < 0)
823 goto file_err;
824 goto data_err;
825 }
826 reply(226, "Transfer complete.");
827 return;
828 default:
829 transflag = 0;
830 reply(550, "Unimplemented TYPE %d in send_data", type);
831 return;
832 }
833
834data_err:
835 transflag = 0;
836 perror_reply(426, "Data connection");
837 return;
838
839file_err:
840 transflag = 0;
841 perror_reply(551, "Error on input file");
842}
843
844/*
845 * Transfer data from peer to
846 * "outstr" using the appropriate
847 * encapulation of the data subject
848 * to Mode, Structure, and Type.
849 *
850 * N.B.: Form isn't handled.
851 */
852receive_data(instr, outstr)
853 FILE *instr, *outstr;
854{
855 register int c;
856 int cnt, bare_lfs = 0;
857 char buf[BUFSIZ];
858
859 transflag++;
860 if (setjmp(urgcatch)) {
861 transflag = 0;
862 return (-1);
863 }
864 switch (type) {
865
866 case TYPE_I:
867 case TYPE_L:
868 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
869 if (write(fileno(outstr), buf, cnt) != cnt)
870 goto file_err;
871 byte_count += cnt;
872 }
873 if (cnt < 0)
874 goto data_err;
875 transflag = 0;
876 return (0);
877
878 case TYPE_E:
879 reply(553, "TYPE E not implemented.");
880 transflag = 0;
881 return (-1);
882
883 case TYPE_A:
884 while ((c = getc(instr)) != EOF) {
885 byte_count++;
886 if (c == '\n')
887 bare_lfs++;
888 while (c == '\r') {
889 if (ferror(outstr))
890 goto data_err;
891 if ((c = getc(instr)) != '\n') {
892 (void) putc ('\r', outstr);
893 if (c == '\0' || c == EOF)
894 goto contin2;
895 }
896 }
897 (void) putc(c, outstr);
898 contin2: ;
899 }
900 fflush(outstr);
901 if (ferror(instr))
902 goto data_err;
903 if (ferror(outstr))
904 goto file_err;
905 transflag = 0;
906 if (bare_lfs) {
907 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
908 printf(" File may not have transferred correctly.\r\n");
909 }
910 return (0);
911 default:
912 reply(550, "Unimplemented TYPE %d in receive_data", type);
913 transflag = 0;
914 return (-1);
915 }
916
917data_err:
918 transflag = 0;
919 perror_reply(426, "Data Connection");
920 return (-1);
921
922file_err:
923 transflag = 0;
924 perror_reply(452, "Error writing file");
925 return (-1);
926}
927
928statfilecmd(filename)
929 char *filename;
930{
931 char line[BUFSIZ];
932 FILE *fin;
933 int c;
934
935 (void) sprintf(line, "/bin/ls -lgA %s", filename);
936 fin = ftpd_popen(line, "r");
937 lreply(211, "status of %s:", filename);
938 while ((c = getc(fin)) != EOF) {
939 if (c == '\n') {
940 if (ferror(stdout)){
941 perror_reply(421, "control connection");
942 (void) ftpd_pclose(fin);
943 dologout(1);
944 /* NOTREACHED */
945 }
946 if (ferror(fin)) {
947 perror_reply(551, filename);
948 (void) ftpd_pclose(fin);
949 return;
950 }
951 (void) putc('\r', stdout);
952 }
953 (void) putc(c, stdout);
954 }
955 (void) ftpd_pclose(fin);
956 reply(211, "End of Status");
957}
958
959statcmd()
960{
961 struct sockaddr_in *sin;
962 u_char *a, *p;
963
964 lreply(211, "%s FTP server status:", hostname, version);
965 printf(" %s\r\n", version);
966 printf(" Connected to %s", remotehost);
967 if (!isdigit(remotehost[0]))
968 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
969 printf("\r\n");
970 if (logged_in) {
971 if (guest)
972 printf(" Logged in anonymously\r\n");
973 else
974 printf(" Logged in as %s\r\n", pw->pw_name);
975 } else if (askpasswd)
976 printf(" Waiting for password\r\n");
977 else
978 printf(" Waiting for user name\r\n");
979 printf(" TYPE: %s", typenames[type]);
980 if (type == TYPE_A || type == TYPE_E)
981 printf(", FORM: %s", formnames[form]);
982 if (type == TYPE_L)
983#if NBBY == 8
984 printf(" %d", NBBY);
985#else
986 printf(" %d", bytesize); /* need definition! */
987#endif
988 printf("; STRUcture: %s; transfer MODE: %s\r\n",
989 strunames[stru], modenames[mode]);
990 if (data != -1)
991 printf(" Data connection open\r\n");
992 else if (pdata != -1) {
993 printf(" in Passive mode");
994 sin = &pasv_addr;
995 goto printaddr;
996 } else if (usedefault == 0) {
997 printf(" PORT");
998 sin = &data_dest;
999printaddr:
1000 a = (u_char *) &sin->sin_addr;
1001 p = (u_char *) &sin->sin_port;
1002#define UC(b) (((int) b) & 0xff)
1003 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1004 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1005#undef UC
1006 } else
1007 printf(" No data connection\r\n");
1008 reply(211, "End of status");
1009}
1010
1011fatal(s)
1012 char *s;
1013{
1014 reply(451, "Error in server: %s\n", s);
1015 reply(221, "Closing connection due to server error.");
1016 dologout(0);
1017 /* NOTREACHED */
1018}
1019
1020/* VARARGS2 */
1021reply(n, fmt, p0, p1, p2, p3, p4, p5)
1022 int n;
1023 char *fmt;
1024{
1025 printf("%d ", n);
1026 printf(fmt, p0, p1, p2, p3, p4, p5);
1027 printf("\r\n");
1028 (void)fflush(stdout);
1029 if (debug) {
1030 syslog(LOG_DEBUG, "<--- %d ", n);
1031 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
1032}
1033}
1034
1035/* VARARGS2 */
1036lreply(n, fmt, p0, p1, p2, p3, p4, p5)
1037 int n;
1038 char *fmt;
1039{
1040 printf("%d- ", n);
1041 printf(fmt, p0, p1, p2, p3, p4, p5);
1042 printf("\r\n");
1043 (void)fflush(stdout);
1044 if (debug) {
1045 syslog(LOG_DEBUG, "<--- %d- ", n);
1046 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
1047 }
1048}
1049
1050ack(s)
1051 char *s;
1052{
1053 reply(250, "%s command successful.", s);
1054}
1055
1056nack(s)
1057 char *s;
1058{
1059 reply(502, "%s command not implemented.", s);
1060}
1061
1062/* ARGSUSED */
1063yyerror(s)
1064 char *s;
1065{
1066 char *cp;
1067
1068 if (cp = index(cbuf,'\n'))
1069 *cp = '\0';
1070 reply(500, "'%s': command not understood.", cbuf);
1071}
1072
1073delete(name)
1074 char *name;
1075{
1076 struct stat st;
1077
1078 if (stat(name, &st) < 0) {
1079 perror_reply(550, name);
1080 return;
1081 }
1082 if ((st.st_mode&S_IFMT) == S_IFDIR) {
1083 if (rmdir(name) < 0) {
1084 perror_reply(550, name);
1085 return;
1086 }
1087 goto done;
1088 }
1089 if (unlink(name) < 0) {
1090 perror_reply(550, name);
1091 return;
1092 }
1093done:
1094 ack("DELE");
1095}
1096
1097cwd(path)
1098 char *path;
1099{
1100 if (chdir(path) < 0)
1101 perror_reply(550, path);
1102 else
1103 ack("CWD");
1104}
1105
1106makedir(name)
1107 char *name;
1108{
1109 if (mkdir(name, 0777) < 0)
1110 perror_reply(550, name);
1111 else
1112 reply(257, "MKD command successful.");
1113}
1114
1115removedir(name)
1116 char *name;
1117{
1118 if (rmdir(name) < 0)
1119 perror_reply(550, name);
1120 else
1121 ack("RMD");
1122}
1123
1124pwd()
1125{
1126 char path[MAXPATHLEN + 1];
1127 extern char *getwd();
1128
1129 if (getwd(path) == (char *)NULL)
1130 reply(550, "%s.", path);
1131 else
1132 reply(257, "\"%s\" is current directory.", path);
1133}
1134
1135char *
1136renamefrom(name)
1137 char *name;
1138{
1139 struct stat st;
1140
1141 if (stat(name, &st) < 0) {
1142 perror_reply(550, name);
1143 return ((char *)0);
1144 }
1145 reply(350, "File exists, ready for destination name");
1146 return (name);
1147}
1148
1149renamecmd(from, to)
1150 char *from, *to;
1151{
1152 if (rename(from, to) < 0)
1153 perror_reply(550, "rename");
1154 else
1155 ack("RNTO");
1156}
1157
1158dolog(sin)
1159 struct sockaddr_in *sin;
1160{
1161 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
1162 sizeof (struct in_addr), AF_INET);
1163 time_t t, time();
1164 extern char *ctime();
1165
1166 if (hp)
1167 (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
1168 else
1169 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
1170 sizeof (remotehost));
1171#ifdef SETPROCTITLE
1172 sprintf(proctitle, "%s: connected", remotehost);
1173 setproctitle(proctitle);
1174#endif /* SETPROCTITLE */
1175
1176 if (logging) {
1177 t = time((time_t *) 0);
1178 syslog(LOG_INFO, "connection from %s at %s",
1179 remotehost, ctime(&t));
1180 }
1181}
1182
1183/*
1184 * Record logout in wtmp file
1185 * and exit with supplied status.
1186 */
1187dologout(status)
1188 int status;
1189{
1190 if (logged_in) {
1191 (void) seteuid((uid_t)0);
1192 logwtmp(ttyline, "", "");
1193 }
1194 /* beware of flushing buffers after a SIGPIPE */
1195 _exit(status);
1196}
1197
1198void
1199myoob()
1200{
1201 char *cp;
1202
1203 /* only process if transfer occurring */
1204 if (!transflag)
1205 return;
1206 cp = tmpline;
1207 if (getline(cp, 7, stdin) == NULL) {
1208 reply(221, "You could at least say goodbye.");
1209 dologout(0);
1210 }
1211 upper(cp);
1212 if (strcmp(cp, "ABOR\r\n") == 0) {
1213 tmpline[0] = '\0';
1214 reply(426, "Transfer aborted. Data connection closed.");
1215 reply(226, "Abort successful");
1216 longjmp(urgcatch, 1);
1217 }
1218 if (strcmp(cp, "STAT\r\n") == 0) {
1219 if (file_size != (off_t) -1)
1220 reply(213, "Status: %lu of %lu bytes transferred",
1221 byte_count, file_size);
1222 else
1223 reply(213, "Status: %lu bytes transferred", byte_count);
1224 }
1225}
1226
1227/*
1228 * Note: a response of 425 is not mentioned as a possible response to
1229 * the PASV command in RFC959. However, it has been blessed as
1230 * a legitimate response by Jon Postel in a telephone conversation
1231 * with Rick Adams on 25 Jan 89.
1232 */
1233passive()
1234{
1235 int len;
1236 register char *p, *a;
1237
1238 pdata = socket(AF_INET, SOCK_STREAM, 0);
1239 if (pdata < 0) {
1240 perror_reply(425, "Can't open passive connection");
1241 return;
1242 }
1243 pasv_addr = ctrl_addr;
1244 pasv_addr.sin_port = 0;
1245 (void) seteuid((uid_t)0);
1246 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
1247 (void) seteuid((uid_t)pw->pw_uid);
1248 goto pasv_error;
1249 }
1250 (void) seteuid((uid_t)pw->pw_uid);
1251 len = sizeof(pasv_addr);
1252 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
1253 goto pasv_error;
1254 if (listen(pdata, 1) < 0)
1255 goto pasv_error;
1256 a = (char *) &pasv_addr.sin_addr;
1257 p = (char *) &pasv_addr.sin_port;
1258
1259#define UC(b) (((int) b) & 0xff)
1260
1261 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1262 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1263 return;
1264
1265pasv_error:
1266 (void) close(pdata);
1267 pdata = -1;
1268 perror_reply(425, "Can't open passive connection");
1269 return;
1270}
1271
1272/*
1273 * Generate unique name for file with basename "local".
1274 * The file named "local" is already known to exist.
1275 * Generates failure reply on error.
1276 */
1277char *
1278gunique(local)
1279 char *local;
1280{
1281 static char new[MAXPATHLEN];
1282 struct stat st;
1283 char *cp = rindex(local, '/');
1284 int count = 0;
1285
1286 if (cp)
1287 *cp = '\0';
1288 if (stat(cp ? local : ".", &st) < 0) {
1289 perror_reply(553, cp ? local : ".");
1290 return((char *) 0);
1291 }
1292 if (cp)
1293 *cp = '/';
1294 (void) strcpy(new, local);
1295 cp = new + strlen(new);
1296 *cp++ = '.';
1297 for (count = 1; count < 100; count++) {
1298 (void) sprintf(cp, "%d", count);
1299 if (stat(new, &st) < 0)
1300 return(new);
1301 }
1302 reply(452, "Unique file name cannot be created.");
1303 return((char *) 0);
1304}
1305
1306/*
1307 * Format and send reply containing system error number.
1308 */
1309perror_reply(code, string)
1310 int code;
1311 char *string;
1312{
1313 reply(code, "%s: %s.", string, strerror(errno));
1314}
1315
1316static char *onefile[] = {
1317 "",
1318 0
1319};
1320
1321send_file_list(whichfiles)
1322 char *whichfiles;
1323{
1324 struct stat st;
1325 DIR *dirp = NULL;
1326 struct dirent *dir;
1327 FILE *dout = NULL;
1328 register char **dirlist, *dirname;
1329 int simple = 0;
1330 char *strpbrk();
1331
1332 if (strpbrk(whichfiles, "~{[*?") != NULL) {
1333 extern char **ftpglob(), *globerr;
1334
1335 globerr = NULL;
1336 dirlist = ftpglob(whichfiles);
1337 if (globerr != NULL) {
1338 reply(550, globerr);
1339 return;
1340 } else if (dirlist == NULL) {
1341 errno = ENOENT;
1342 perror_reply(550, whichfiles);
1343 return;
1344 }
1345 } else {
1346 onefile[0] = whichfiles;
1347 dirlist = onefile;
1348 simple = 1;
1349 }
1350
1351 if (setjmp(urgcatch)) {
1352 transflag = 0;
1353 return;
1354 }
1355 while (dirname = *dirlist++) {
1356 if (stat(dirname, &st) < 0) {
1357 /*
1358 * If user typed "ls -l", etc, and the client
1359 * used NLST, do what the user meant.
1360 */
1361 if (dirname[0] == '-' && *dirlist == NULL &&
1362 transflag == 0) {
1363 retrieve("/bin/ls %s", dirname);
1364 return;
1365 }
1366 perror_reply(550, whichfiles);
1367 if (dout != NULL) {
1368 (void) fclose(dout);
1369 transflag = 0;
1370 data = -1;
1371 pdata = -1;
1372 }
1373 return;
1374 }
1375
1376 if ((st.st_mode&S_IFMT) == S_IFREG) {
1377 if (dout == NULL) {
1378 dout = dataconn("file list", (off_t)-1, "w");
1379 if (dout == NULL)
1380 return;
1381 transflag++;
1382 }
1383 fprintf(dout, "%s%s\n", dirname,
1384 type == TYPE_A ? "\r" : "");
1385 byte_count += strlen(dirname) + 1;
1386 continue;
1387 } else if ((st.st_mode&S_IFMT) != S_IFDIR)
1388 continue;
1389
1390 if ((dirp = opendir(dirname)) == NULL)
1391 continue;
1392
1393 while ((dir = readdir(dirp)) != NULL) {
1394 char nbuf[MAXPATHLEN];
1395
1396 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1397 continue;
1398 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1399 dir->d_namlen == 2)
1400 continue;
1401
1402 sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1403
1404 /*
1405 * We have to do a stat to insure it's
1406 * not a directory or special file.
1407 */
1408 if (simple || (stat(nbuf, &st) == 0 &&
1409 (st.st_mode&S_IFMT) == S_IFREG)) {
1410 if (dout == NULL) {
1411 dout = dataconn("file list", (off_t)-1,
1412 "w");
1413 if (dout == NULL)
1414 return;
1415 transflag++;
1416 }
1417 if (nbuf[0] == '.' && nbuf[1] == '/')
1418 fprintf(dout, "%s%s\n", &nbuf[2],
1419 type == TYPE_A ? "\r" : "");
1420 else
1421 fprintf(dout, "%s%s\n", nbuf,
1422 type == TYPE_A ? "\r" : "");
1423 byte_count += strlen(nbuf) + 1;
1424 }
1425 }
1426 (void) closedir(dirp);
1427 }
1428
1429 if (dout == NULL)
1430 reply(550, "No files found.");
1431 else if (ferror(dout) != 0)
1432 perror_reply(550, "Data connection");
1433 else
1434 reply(226, "Transfer complete.");
1435
1436 transflag = 0;
1437 if (dout != NULL)
1438 (void) fclose(dout);
1439 data = -1;
1440 pdata = -1;
1441}
1442
1443#ifdef SETPROCTITLE
1444/*
1445 * clobber argv so ps will show what we're doing.
1446 * (stolen from sendmail)
1447 * warning, since this is usually started from inetd.conf, it
1448 * often doesn't have much of an environment or arglist to overwrite.
1449 */
1450
1451/*VARARGS1*/
1452setproctitle(fmt, a, b, c)
1453char *fmt;
1454{
1455 register char *p, *bp, ch;
1456 register int i;
1457 char buf[BUFSIZ];
1458
1459 (void) sprintf(buf, fmt, a, b, c);
1460
1461 /* make ps print our process name */
1462 p = Argv[0];
1463 *p++ = '-';
1464
1465 i = strlen(buf);
1466 if (i > LastArgv - p - 2) {
1467 i = LastArgv - p - 2;
1468 buf[i] = '\0';
1469 }
1470 bp = buf;
1471 while (ch = *bp++)
1472 if (ch != '\n' && ch != '\r')
1473 *p++ = ch;
1474 while (p < LastArgv)
1475 *p++ = ' ';
1476}
1477#endif /* SETPROCTITLE */