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