|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)ftpd.c 4.28 (Berkeley) 9/22/83";
3: #endif
4:
5: #define CHOOSE_PORT
6: #define DEBUG
7: /*
8: * FTP server.
9: */
10: #include <sys/param.h>
11: #include <sys/stat.h>
12: #include <sys/ioctl.h>
13: #include <sys/file.h>
14: #include <wait.h>
15:
16: #include <sys/inet/in.h>
17: #include <sys/inet/tcp_user.h>
18:
19: #include "ftp.h"
20:
21: #include <stdio.h>
22: #include <signal.h>
23: #include <pwd.h>
24: #include <setjmp.h>
25: #include <errno.h>
26:
27: /*
28: * File containing login names
29: * NOT to be used on this machine.
30: * Commonly used to disallow uucp.
31: */
32: #define FTPUSERS "/etc/ftpusers"
33:
34: extern int errno;
35: extern char *sys_errlist[];
36: extern char *crypt();
37: extern char version[];
38: extern char *home; /* pointer to home directory for glob */
39: extern FILE *popen(), *fopen();
40: extern int pclose(), fclose();
41:
42: struct socket {
43: unsigned short sport;
44: long saddr;
45: } his_addr, data_dest, data_source, ctrl_addr;
46: #define SO_KEEPALIVE 0x2
47:
48: int data;
49: jmp_buf errcatch;
50: int logged_in;
51: struct passwd *pw;
52: int debug;
53: int timeout;
54: int logging;
55: int guest;
56: int type;
57: int form;
58: int stru; /* avoid C keyword */
59: int mode;
60: int usedefault = 1; /* for data transfers */
61: char hostname[32];
62: char remotehost[32];
63: int pasv;
64:
65: /*
66: * Timeout intervals for retrying connections
67: * to hosts that don't accept PORT cmds. This
68: * is a kludge, but given the problems with TCP...
69: */
70: #define SWAITMAX 90 /* wait at most 90 seconds */
71: #define SWAITINT 5 /* interval between retries */
72:
73: int swaitmax = SWAITMAX;
74: int swaitint = SWAITINT;
75:
76: int lostconn();
77: int reapchild();
78: FILE *getdatasock();
79: FILE *dataconn();
80:
81: main(argc, argv)
82: int argc;
83: char *argv[];
84: {
85: int ctrl, s, options = 0;
86: char *cp;
87: int dev;
88: int status;
89: struct in_service *servp;
90:
91: servp = in_service("ftp", "tcp", 0);
92: if (servp == 0) {
93: fprintf(stderr, "ftpd: ftp/tcp: unknown service\n");
94: exit(1);
95: }
96: ctrl_addr.sport = servp->port;
97: data_source.sport = servp->port - 1;
98:
99: debug = 0;
100: argc--, argv++;
101: while (argc > 0 && *argv[0] == '-') {
102: for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
103:
104: case 'v':
105: debug = 1;
106: break;
107:
108: case 'd':
109: debug = 1;
110: break;
111:
112: case 'l':
113: logging = 1;
114: break;
115:
116: case 't':
117: timeout = atoi(++cp);
118: goto nextopt;
119: break;
120:
121: default:
122: fprintf(stderr, "Unknown flag -%c ignored.\n", *cp);
123: break;
124: }
125: nextopt:
126: argc--, argv++;
127: }
128:
129: #ifndef DEBUG
130: if (fork())
131: exit(0);
132: for (s = 0; s < 10; s++)
133: if (!logging || (s != 2))
134: (void) close(s);
135: (void) open("/", 0);
136: (void) dup2(0, 1);
137: if (!logging)
138: (void) dup2(0, 2);
139: { int tt = open("/dev/tty", 2);
140: if (tt > 0) {
141: /* ioctl(tt, TIOCNOTTY, 0); */
142: close(tt);
143: }
144: }
145: #endif
146: signal(SIGPIPE, SIG_IGN); /* used to be lostconn */
147: signal(SIGHUP, SIG_IGN);
148:
149: fprintf(stderr,"ftpd started: options (debug,logging,timeout) are (%d,%d,%d)\n", debug, logging, timeout);
150: do {
151: struct tcpuser tu;
152:
153: s = tcp_sock();
154: if (s < 0) {
155: perror("ftpd: socket");;
156: sleep(5);
157: continue;
158: }
159: tu.lport = servp->port;
160: tu.laddr = 0;
161: tu.fport = 0;
162: tu.faddr = 0;
163: tu.param = SO_KEEPALIVE;
164: if (tcp_listen(s, &tu) < 0) {
165: close(s);
166: s = -1;
167: sleep(30);
168: continue;
169: }
170: } while (s < 0);
171:
172: for (;;) {
173: int ctrl;
174: struct tcpuser tu;
175:
176: tu.lport = 0;
177: tu.laddr = 0;
178: tu.fport = 0;
179: tu.faddr = 0;
180: tu.param = 0;
181: ctrl = tcp_accept(s, &tu);
182: his_addr.saddr = tu.faddr;
183: his_addr.sport = tu.fport;
184: ctrl_addr.saddr = tu.laddr;
185: ctrl_addr.sport = tu.lport;
186: dev = tu.param;
187: fprintf(stderr, "accept req from %s, port %d\n", inet_ntoa(his_addr.saddr), his_addr.sport);
188: if (ctrl < 0) {
189: if (errno == EINTR)
190: continue;
191: perror("ftpd: accept");
192: sleep(1);
193: continue;
194: }
195: while (wait3(&status, WNOHANG, 0) > 0)
196: ;
197: signal(SIGCHLD, reapchild);
198: switch(fork()) {
199: case -1:
200: printf("Out of processes\n");
201: break;
202: case 0:
203: signal (SIGCHLD, SIG_IGN);
204: dolog(&his_addr);
205: close(s);
206: dup2(ctrl, 0), close(ctrl), dup2(0, 1);
207: /*
208: * Set up default state
209: */
210: logged_in = 0;
211: data = -1;
212: type = TYPE_A;
213: form = FORM_N;
214: stru = STRU_F;
215: mode = MODE_S;
216: /* try to do this equivalently
217: (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr));
218: gethostname(hostname, sizeof (hostname));
219: */
220: reply(220, "%s FTP server (%s) ready.",
221: hostname, version);
222: for (;;) {
223: setjmp(errcatch);
224: yyparse();
225: fprintf(stderr, "out of yyparse\n");
226: }
227: }
228: close(ctrl);
229: }
230: }
231:
232: reapchild()
233: {
234: union wait status;
235:
236: while (wait3(&status, WNOHANG, 0) > 0)
237: ;
238: }
239:
240: lostconn()
241: {
242:
243: if (debug)
244: fprintf(stderr, "Lost connection.\n");
245: dologout(-1);
246: }
247:
248: pass(passwd)
249: char *passwd;
250: {
251: char *xpasswd, *savestr();
252: static struct passwd save;
253:
254: if (logged_in || pw == NULL) {
255: reply(503, "Login with USER first.");
256: return;
257: }
258: if (!guest) { /* "ftp" is only account allowed no password */
259: xpasswd = crypt(passwd, pw->pw_passwd);
260: if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
261: reply(530, "Login incorrect.");
262: pw = NULL;
263: return;
264: }
265: }
266: /*
267: setgid(pw->pw_gid);
268: initgroups(pw->pw_name, pw->pw_gid);
269: */
270: if (chdir(pw->pw_dir)) {
271: reply(550, "User %s: can't change directory to $s.",
272: pw->pw_name, pw->pw_dir);
273: goto bad;
274: }
275: if (guest && chroot(pw->pw_dir) < 0) {
276: reply(550, "Can't set guest privileges.");
277: goto bad;
278: }
279: if (!guest)
280: reply(230, "User %s logged in.", pw->pw_name);
281: else
282: reply(230, "Guest login ok, access restrictions apply.");
283: logged_in = 1;
284: dologin(pw);
285: /* setuid(pw->pw_uid); */
286: /*
287: * Save everything so globbing doesn't
288: * clobber the fields.
289: */
290: save = *pw;
291: save.pw_name = savestr(pw->pw_name);
292: save.pw_passwd = savestr(pw->pw_passwd);
293: save.pw_comment = savestr(pw->pw_comment);
294: save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
295: save.pw_dir = savestr(pw->pw_dir);
296: save.pw_shell = savestr(pw->pw_shell);
297: pw = &save;
298: home = pw->pw_dir; /* home dir for globbing */
299: return;
300: bad:
301: /* setuid(0); */
302: pw = NULL;
303: }
304:
305: char *
306: savestr(s)
307: char *s;
308: {
309: char *malloc();
310: char *new = malloc(strlen(s) + 1);
311:
312: if (new != NULL)
313: strcpy(new, s);
314: return (new);
315: }
316:
317:
318: openfile(name)
319: char *name;
320: {
321: reply(550, "%s: can't open", name);
322: }
323:
324: retrieve(cmd, name)
325: char *cmd, *name;
326: {
327: FILE *fin, *dout;
328: struct stat st;
329: int (*closefunc)();
330:
331: if (cmd == 0) {
332: #ifdef notdef
333: /* no remote command execution -- it's a security hole */
334: if (*name == '|')
335: fin = popen(name + 1, "r"), closefunc = pclose;
336: else
337: #endif
338: if (-1 == access2(name, 4, pw->pw_uid, pw->pw_gid)) {
339: fprintf(stderr, "can't access %s\n", name);
340: fin = NULL;
341: } else
342: fin = fopen(name, "r"), closefunc = fclose;
343: } else {
344: char line[BUFSIZ];
345:
346: sprintf(line, cmd, name), name = line;
347: fprintf(stderr,"cmd is %s\n", line);
348: fin = popen(line, "r"), closefunc = pclose;
349: }
350: if (fin == NULL) {
351: fprintf(stderr, "bad popen()\n");
352: if (errno != 0)
353: reply(550, "%s: %s.", name, sys_errlist[errno]);
354: return;
355: }
356:
357: st.st_size = 0;
358: if (cmd == 0 &&
359: (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
360: reply(550, "%s: not a plain file.", name);
361: goto done;
362: }
363: dout = dataconn(name, st.st_size, "w");
364: if (dout == NULL)
365: goto done;
366: if (send_data(fin, dout) || ferror(dout))
367: reply(550, "%s: %s.", name, sys_errlist[errno]);
368:
369: else {
370: fclose(dout);
371: delay(timeout);
372: reply(226, "Transfer complete.");
373: }
374: data = -1;
375: done:
376: (*closefunc)(fin);
377: }
378:
379: store(name, mode)
380: char *name, *mode;
381: {
382: FILE *fout, *din;
383: int (*closefunc)(), dochown = 0;
384:
385: #ifdef notdef
386: /* no remote command execution -- it's a security hole */
387:
388: if (name[0] == '|')
389: fout = popen(&name[1], "w"), closefunc = pclose;
390: else
391: #endif
392: {
393: struct stat st;
394:
395: if (stat(name, &st) < 0)
396: dochown++;
397: if (-1 == access2(name, 2, pw->pw_uid, pw->pw_gid))
398: fout = NULL;
399: else
400: fout = fopen(name, mode), closefunc = fclose;
401: }
402: if (fout == NULL) {
403: reply(550, "%s: %s.", name, sys_errlist[errno]);
404: return;
405: }
406: din = dataconn(name, (off_t)-1, "r");
407: if (din == NULL)
408: goto done;
409: if (receive_data(din, fout) || ferror(fout))
410: reply(550, "%s: %s.", name, sys_errlist[errno]);
411: else {
412: fflush(fout);
413: delay(timeout);
414: reply(226, "Transfer complete.");
415: }
416: fclose(din);
417: data = -1;
418: done:
419: if (dochown)
420: (void) chown(name, pw->pw_uid, -1);
421: (*closefunc)(fout);
422: }
423: FILE *
424: getdatasock(mode)
425: char *mode;
426: {
427: int s;
428:
429: if (data >= 0)
430: return (fdopen(data, mode));
431: data_source.saddr = ctrl_addr.saddr;
432: s = tcp_sock();
433: if (s < 0) {
434: perror("ftpd: socket");
435: return(NULL);
436: }
437: return (fdopen(s, mode));
438: }
439:
440: FILE *
441: dataconn(name, size, mode)
442: char *name;
443: off_t size;
444: char *mode;
445: {
446: char sizebuf[32];
447: FILE *file;
448: int retry = 0;
449: struct tcpuser tu;
450:
451: if (size >= 0)
452: sprintf (sizebuf, " (%ld bytes)", size);
453: else
454: (void) strcpy(sizebuf, "");
455: if (data >= 0) {
456: reply(125, "Using existing data connection for %s%s.",
457: name, sizebuf);
458: usedefault = 1;
459: return (fdopen(data, mode));
460: }
461: if (usedefault)
462: data_dest = his_addr;
463: usedefault = 1;
464: file = getdatasock(mode);
465: if (file == NULL) {
466: reply(425, "Can't create data socket (%s,%d): %s.",
467: inet_ntoa(data_source.saddr),
468: ntohs(data_source.sport),
469: sys_errlist[errno]);
470: return (NULL);
471: }
472: reply(150, "Opening data connection for %s (%s,%d)%s.",
473: name,
474: inet_ntoa(data_dest.saddr),
475: data_dest.sport, sizebuf);
476:
477: data = fileno(file);
478: tu.lport = pasv?0:data_source.sport;
479: tu.laddr = 0;
480: tu.fport = data_dest.sport;
481: tu.faddr = data_dest.saddr;
482: tu.param = 0;
483: while (tcp_connect(data, &tu) < 0) {
484: if (retry < swaitmax) {
485: sleep(swaitint);
486: retry += swaitint;
487: continue;
488: }
489: reply(425, "Can't build data connection: %s.",
490: sys_errlist[errno]);
491: (void)fclose(file);
492: data = -1;
493: return (NULL);
494: }
495: return (file);
496: }
497:
498: /*
499: * Tranfer the contents of "instr" to
500: * "outstr" peer using the appropriate
501: * encapulation of the date subject
502: * to Mode, Structure, and Type.
503: *
504: * NB: Form isn't handled.
505: */
506: send_data(instr, outstr)
507: FILE *instr, *outstr;
508: {
509: register int c;
510: int netfd, filefd, cnt = 0;
511: char buf[BUFSIZ];
512:
513: switch (type) {
514:
515: case TYPE_A:
516: while ((c = getc(instr)) != EOF) {
517: if (c == '\n') {
518: if (ferror (outstr))
519: return (1);
520: putc('\r', outstr);
521: }
522: putc(c, outstr);
523: if (c == '\r')
524: putc ('\0', outstr);
525: /* cnt++;
526: if (!(cnt%1000)) { fprintf(stderr, "%d ", cnt); fflush(stderr); } */
527: }
528: if (ferror (instr) || ferror (outstr)) {
529: fprintf(stderr,"error: inst %d, outstr %d\n", ferror(instr), ferror(outstr));
530: return (1);
531: }
532: return (0);
533:
534: case TYPE_I:
535: case TYPE_L:
536: netfd = fileno(outstr);
537: filefd = fileno(instr);
538:
539: while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
540: if (write(netfd, buf, cnt) < 0) {
541: fprintf(stderr, "write error, cnt=%d\n", cnt);
542: return (1);
543: }
544: return (cnt < 0);
545: }
546: reply(504,"Unimplemented TYPE %d in send_data", type);
547: return (1);
548: }
549:
550: /*
551: * Transfer data from peer to
552: * "outstr" using the appropriate
553: * encapulation of the data subject
554: * to Mode, Structure, and Type.
555: *
556: * N.B.: Form isn't handled.
557: */
558: receive_data(instr, outstr)
559: FILE *instr, *outstr;
560: {
561: register int c;
562: int cnt;
563: char buf[BUFSIZ];
564:
565:
566: switch (type) {
567:
568: case TYPE_I:
569: case TYPE_L:
570: while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
571: if (write(fileno(outstr), buf, cnt) < 0)
572: return (1);
573: return (cnt < 0);
574:
575: case TYPE_E:
576: reply(504, "TYPE E not implemented.");
577: return (1);
578:
579: case TYPE_A:
580: while ((c = getc(instr)) != EOF) {
581: if (c == '\r') {
582: if (ferror (outstr))
583: return (1);
584: if ((c = getc(instr)) != '\n')
585: putc ('\r', outstr);
586: if (c == '\0')
587: continue;
588: }
589: putc (c, outstr);
590: }
591: if (ferror (instr) || ferror (outstr))
592: return (1);
593: return (0);
594: }
595: fatal("Unknown type in receive_data.");
596: /*NOTREACHED*/
597: }
598:
599: fatal(s)
600: char *s;
601: {
602: reply(451, "Error in server: %s\n", s);
603: reply(221, "Closing connection due to server error.");
604: dologout(0);
605: }
606:
607: reply(n, s, args)
608: int n;
609: char *s;
610: {
611:
612: printf("%d ", n);
613: _doprnt(s, &args, stdout);
614: printf("\r\n");
615: fflush(stdout);
616: if (debug) {
617: fprintf(stderr, "<--- %d ", n);
618: _doprnt(s, &args, stderr);
619: fprintf(stderr, "\n");
620: fflush(stderr);
621: }
622: }
623:
624: lreply(n, s, args)
625: int n;
626: char *s;
627: {
628: printf("%d-", n);
629: _doprnt(s, &args, stdout);
630: printf("\r\n");
631: fflush(stdout);
632: if (debug) {
633: fprintf(stderr, "<--- %d-", n);
634: _doprnt(s, &args, stderr);
635: fprintf(stderr, "\n");
636: }
637: }
638:
639: replystr(s)
640: char *s;
641: {
642: printf("%s\r\n", s);
643: fflush(stdout);
644: if (debug)
645: fprintf(stderr, "<--- %s\n", s);
646: }
647:
648: ack(s)
649: char *s;
650: {
651: reply(200, "%s command okay.", s);
652: }
653:
654: nack(s)
655: char *s;
656: {
657: reply(502, "%s command not implemented.", s);
658: }
659:
660: yyerror()
661: {
662: reply(500, "Command not understood.");
663: }
664:
665: delete(name)
666: char *name;
667: {
668: struct stat st;
669:
670: if (stat(name, &st) < 0) {
671: reply(550, "%s: %s.", name, sys_errlist[errno]);
672: return;
673: }
674: if ((st.st_mode&S_IFMT) == S_IFDIR) {
675: if (rmdir(name) < 0) {
676: reply(550, "%s: %s.", name, sys_errlist[errno]);
677: return;
678: }
679: goto done;
680: }
681: if (unlink(name) < 0) {
682: reply(550, "%s: %s.", name, sys_errlist[errno]);
683: return;
684: }
685: done:
686: ack("DELE");
687: }
688:
689: cwd(path)
690: char *path;
691: {
692:
693: if (chdir(path) < 0) {
694: reply(550, "%s: %s.", path, sys_errlist[errno]);
695: return;
696: }
697: ack("CWD");
698: }
699:
700: makedir(name)
701: char *name;
702: {
703: struct stat st;
704: int dochown = stat(name, &st) < 0;
705:
706: if (mkdir(name, 0777) < 0) {
707: reply(550, "%s: %s.", name, sys_errlist[errno]);
708: return;
709: }
710: if (dochown)
711: (void) chown(name, pw->pw_uid, -1);
712: ack("MKDIR");
713: }
714:
715: removedir(name)
716: char *name;
717: {
718:
719: if (rmdir(name) < 0) {
720: reply(550, "%s: %s.", name, sys_errlist[errno]);
721: return;
722: }
723: ack("RMDIR");
724: }
725:
726: #define MAXPATHLEN 256
727: pwd()
728: {
729: char path[MAXPATHLEN + 1];
730:
731: if (getwd(path) == NULL) {
732: reply(451, "%s.", path);
733: return;
734: }
735: reply(251, "\"%s\" is current directory.", path);
736: }
737:
738: char *
739: renamefrom(name)
740: char *name;
741: {
742: struct stat st;
743:
744: if (stat(name, &st) < 0) {
745: reply(550, "%s: %s.", name, sys_errlist[errno]);
746: return ((char *)0);
747: }
748: reply(350, "File exists, ready for destination name");
749: return (name);
750: }
751:
752: renamecmd(from, to)
753: char *from, *to;
754: {
755:
756: if (rename(from, to) < 0) {
757: reply(550, "rename: %s.", sys_errlist[errno]);
758: return;
759: }
760: ack("RNTO");
761: }
762:
763: dolog(addr)
764: register struct socket *addr;
765: {
766: char *h_name = (char *)in_host(addr->saddr);
767: time_t t;
768:
769: strncpy(remotehost, h_name, sizeof (remotehost));
770: if (!logging)
771: return;
772: t = time(0);
773: fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
774: fflush(stderr);
775: }
776:
777: #include <utmp.h>
778:
779: #define SCPYN(a, b) strncpy(a, b, sizeof (a))
780: struct utmp utmp;
781:
782: /*
783: * Record login in wtmp file.
784: */
785:
786: #define O_WRONLY 1
787: #define O_APPEND 1
788: dologin(pw)
789: struct passwd *pw;
790: {
791: int wtmp;
792: char line[32];
793:
794: pasv = 0;
795: wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
796: if (wtmp >= 0) {
797: /* hack, but must be unique and no tty line */
798: sprintf(line, "ftp%d", getpid());
799: SCPYN(utmp.ut_line, line);
800: SCPYN(utmp.ut_name, pw->pw_name);
801: utmp.ut_time = time(0);
802: (void) write(wtmp, (char *)&utmp, sizeof (utmp));
803: (void) close(wtmp);
804: }
805: }
806:
807: /*
808: * Record logout in wtmp file
809: * and exit with supplied status.
810: */
811: dologout(status)
812: int status;
813: {
814: int wtmp;
815:
816: if (!logged_in)
817: _exit(status);
818: wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
819: if (wtmp >= 0) {
820: SCPYN(utmp.ut_name, "");
821: utmp.ut_time = time(0);
822: (void) write(wtmp, (char *)&utmp, sizeof (utmp));
823: (void) close(wtmp);
824: }
825: /* beware of flushing buffers after a SIGPIPE */
826: _exit(status);
827: }
828:
829: static char *
830: nextarg(cpp)
831: char *cpp;
832: {
833: register char *cp = cpp;
834:
835: if (cp == 0)
836: return (cp);
837: while (*cp && *cp != ' ' && *cp != '\t')
838: cp++;
839: if (*cp == ' ' || *cp == '\t') {
840: *cp++ = '\0';
841: while (*cp == ' ' || *cp == '\t')
842: cp++;
843: }
844: if (cp == cpp)
845: return ((char *)0);
846: return (cp);
847: }
848:
849: /*
850: * Check user requesting login priviledges.
851: * Disallow anyone mentioned in the file FTPUSERS
852: * to allow people such as uucp to be avoided.
853: */
854: checkuser(name)
855: register char *name;
856: {
857: char line[BUFSIZ], *strchr();
858: FILE *fd;
859: int found = 0;
860:
861: fd = fopen(FTPUSERS, "r");
862: if (fd == NULL)
863: return (1);
864: while (fgets(line, sizeof (line), fd) != NULL) {
865: register char *cp = strchr(line, '\n');
866:
867: if (cp)
868: *cp = '\0';
869: if (strcmp(line, name) == 0) {
870: found++;
871: break;
872: }
873: }
874: fclose(fd);
875: return (!found);
876: }
877: rename(from, to)
878: {
879: if (-1 == link(from, to))
880: return(-1);
881: return(unlink(from));
882: }
883: inet_ntoa(addr)
884: long addr;
885: {
886: static char b[32];
887: unsigned char *xp = (unsigned char *)&addr;
888:
889: sprintf(b, "%d.%d.%d.%d", xp[3], xp[2], xp[1], xp[0]);
890: return(b);
891: }
892: delay()
893: {
894: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.