Commit | Line | Data |
---|---|---|
53102063 C |
1 | /* ftpd.c - FTAM/FTP gateway */ |
2 | ||
3 | #ifndef lint | |
4 | static char *rcsid = "$Header: /f/osi/ftp-ftam/RCS/ftpd.c,v 7.1 91/02/22 09:24:24 mrose Interim $"; | |
5 | #endif | |
6 | ||
7 | /* | |
8 | * $Header: /f/osi/ftp-ftam/RCS/ftpd.c,v 7.1 91/02/22 09:24:24 mrose Interim $ | |
9 | * | |
10 | * Author: John A. Scott <Scott@GATEWAY.MITRE.ORG> | |
11 | * The MITRE Corporation | |
12 | * Washington C3I Division | |
13 | * 7525 Colshire Drive | |
14 | * Mclean, Virginia 22102 | |
15 | * +1-703-883-5915 | |
16 | * | |
17 | * $Log: ftpd.c,v $ | |
18 | * Revision 7.1 91/02/22 09:24:24 mrose | |
19 | * Interim 6.8 | |
20 | * | |
21 | * Revision 7.0 89/11/23 21:55:21 mrose | |
22 | * Release 6.0 | |
23 | * | |
24 | */ | |
25 | ||
26 | /* | |
27 | * NOTICE | |
28 | * | |
29 | * The MITRE Corporation (hereafter MITRE) makes this software available | |
30 | * on an "as is" basis. No guarantees, either explicit or implied, are | |
31 | * given as to performance or suitability. | |
32 | * | |
33 | */ | |
34 | ||
35 | /* | |
36 | * Shamelessly taken from UCB | |
37 | */ | |
38 | ||
39 | /* | |
40 | * FTP server. | |
41 | */ | |
42 | #include "config.h" | |
43 | #include <sys/param.h> | |
44 | #include <sys/stat.h> | |
45 | #include <sys/ioctl.h> | |
46 | #include <sys/socket.h> | |
47 | #include <sys/file.h> | |
48 | ||
49 | #include <netinet/in.h> | |
50 | ||
51 | #include <arpa/ftp.h> | |
52 | #include <arpa/inet.h> | |
53 | ||
54 | #include <stdio.h> | |
55 | #include <signal.h> | |
56 | #include <pwd.h> | |
57 | #include <setjmp.h> | |
58 | #include <netdb.h> | |
59 | #include <errno.h> | |
60 | #include "general.h" | |
61 | #include "manifest.h" | |
62 | #include "logger.h" | |
63 | extern LLog _ftam_log, *ftam_log; | |
64 | #include <varargs.h> | |
65 | ||
66 | char *ctime(); | |
67 | time_t time(); | |
68 | void adios (), advise (); | |
69 | ||
70 | /* | |
71 | * File containing login names | |
72 | * NOT to be used on this machine. | |
73 | * Commonly used to disallow uucp. | |
74 | */ | |
75 | /* | |
76 | * This may be rework to provide bridge access control. JAS | |
77 | */ | |
78 | #define FTPUSERS "/usr/etc/ftpusers" | |
79 | ||
80 | extern int errno; | |
81 | extern char *sys_errlist[]; | |
82 | extern char ftam_error[]; | |
83 | extern char version[]; | |
84 | ||
85 | struct sockaddr_in ctrl_addr; | |
86 | struct sockaddr_in data_source; | |
87 | struct sockaddr_in data_dest; | |
88 | struct sockaddr_in his_addr; | |
89 | ||
90 | int data; | |
91 | jmp_buf errcatch; | |
92 | int logged_in; | |
93 | int debug = 0; | |
94 | int watch = 1; | |
95 | int timeout; | |
96 | int logging = 0; | |
97 | int type; | |
98 | int form; | |
99 | int stru; /* avoid C keyword */ | |
100 | int mode; | |
101 | int usedefault = 1; /* for data transfers */ | |
102 | char hostname[32]; | |
103 | char remotehost[32]; | |
104 | char *osi_host = NULL; | |
105 | char *ftp_user; | |
106 | char *ftp_account; | |
107 | char *ftp_passwd; | |
108 | int verbose = 0; | |
109 | #define NVEC 100 | |
110 | char *vec[NVEC]; | |
111 | ||
112 | /* | |
113 | * Timeout intervals for retrying connections | |
114 | * to hosts that don't accept PORT cmds. This | |
115 | * is a kludge, but given the problems with TCP... | |
116 | */ | |
117 | #define SWAITMAX 90 /* wait at most 90 seconds */ | |
118 | #define SWAITINT 5 /* interval between retries */ | |
119 | ||
120 | int swaitmax = SWAITMAX; | |
121 | int swaitint = SWAITINT; | |
122 | ||
123 | SFD lostconn(); | |
124 | ||
125 | main(argc, argv) | |
126 | int argc; | |
127 | char *argv[]; | |
128 | { | |
129 | int addrlen; | |
130 | char *ptr, *index(); | |
131 | struct servent *sp; | |
132 | ||
133 | isodetailor (ptr = argv[0], 0); | |
134 | argc--, argv++; | |
135 | if (verbose = isatty (fileno (stderr))) | |
136 | ll_dbinit (ftam_log, ptr); | |
137 | else { | |
138 | ftam_log -> ll_stat &= ~LLOGCLS; | |
139 | ll_hdinit (ftam_log, ptr); | |
140 | } | |
141 | ||
142 | advise (NULLCP, "starting"); | |
143 | ||
144 | addrlen = sizeof his_addr; | |
145 | if (getpeername (0, (struct sockaddr *) &his_addr, &addrlen) == NOTOK) | |
146 | adios ("failed", "getpeername"); | |
147 | sp = getservbyname("ftp", "tcp"); | |
148 | if (sp == 0) { | |
149 | advise(NULLCP, "ftp/tcp: unknown service"); | |
150 | abort(); | |
151 | exit(1); | |
152 | } | |
153 | ctrl_addr.sin_port = sp->s_port; | |
154 | data_source.sin_port = htons(ntohs((u_short) sp->s_port) - 1); | |
155 | (void)signal(SIGPIPE, lostconn); | |
156 | (void)signal(SIGCHLD, SIG_IGN); | |
157 | (void)dup2(0, 1); | |
158 | /* do telnet option negotiation here */ | |
159 | /* | |
160 | * Set up default state | |
161 | */ | |
162 | rcinit(); /* FTAM state initialize */ | |
163 | logged_in = 0; | |
164 | data = -1; | |
165 | type = TYPE_A; | |
166 | form = FORM_N; | |
167 | stru = STRU_F; | |
168 | mode = MODE_S; | |
169 | addrlen = sizeof ctrl_addr; | |
170 | if (getsockname(0, (struct sockaddr *) &ctrl_addr, &addrlen) == NOTOK) | |
171 | adios ("failed", "getsockname"); | |
172 | (void)gethostname(hostname, sizeof (hostname)); | |
173 | ptr = index(hostname,'.'); /* strip off domain name */ | |
174 | if (ptr) *ptr = '\0'; | |
175 | reply(220, "%s FTP/FTAM gateway (%s) ready.", | |
176 | hostname, version); | |
177 | for (;;) { | |
178 | (void)setjmp(errcatch); | |
179 | (void)yyparse(); | |
180 | } | |
181 | } | |
182 | ||
183 | SFD | |
184 | lostconn() | |
185 | { | |
186 | ||
187 | advise (NULLCP,"lost connection"); | |
188 | dologout(-1); | |
189 | } | |
190 | ||
191 | char * | |
192 | savestr(s) | |
193 | char *s; | |
194 | { | |
195 | char *malloc(); | |
196 | char *new = malloc((unsigned) (strlen(s) + 1)); | |
197 | ||
198 | if (new != NULL) | |
199 | (void)strcpy(new, s); | |
200 | return (new); | |
201 | } | |
202 | ||
203 | retrieve(name) | |
204 | char *name; | |
205 | { | |
206 | int result; | |
207 | ||
208 | /* FTAM file retrieval block function. Return values: | |
209 | * OK -- file transfered without error | |
210 | * NOTOK -- file transfer error | |
211 | * DONE -- Problem opening TCP connection for transfer | |
212 | * Error response made by dataconn routine. | |
213 | */ | |
214 | vec[0] = "f_get"; | |
215 | vec[1] = name; | |
216 | vec[2] = NULL; | |
217 | ||
218 | if ((result = f_get(vec)) == NOTOK){ | |
219 | reply(550, "%s: %s.", name, ftam_error); | |
220 | } else if (result == OK) | |
221 | reply(226, "Transfer complete."); | |
222 | ||
223 | data = -1; | |
224 | return; | |
225 | } | |
226 | ||
227 | ftp_store(name, modeX) | |
228 | char *name, *modeX; | |
229 | { | |
230 | int result; | |
231 | /* | |
232 | * f_put is FTAM file storage block function. First arguement | |
233 | * controls file overwrite or append selection. | |
234 | * OK -- file transfered without error | |
235 | * NOTOK -- file transfer error | |
236 | * DONE -- Problem opening TCP connection for transfer | |
237 | * Error response made by dataconn routine. | |
238 | */ | |
239 | ||
240 | vec[0] = strcmp(modeX,"a") ? "put" : "append"; | |
241 | vec[1] = name; | |
242 | vec[2] = NULL; | |
243 | if ((result = f_put(vec)) == NOTOK) | |
244 | reply(550, "%s: %s.", name, ftam_error); | |
245 | else if (result == OK) | |
246 | reply(226, "Transfer complete."); | |
247 | data = -1; | |
248 | } | |
249 | ||
250 | int | |
251 | getdatasock() | |
252 | { | |
253 | /* UCB data socket creation routine */ | |
254 | int s; | |
255 | #ifdef BSD43 | |
256 | int on = 1; | |
257 | #endif | |
258 | ||
259 | if (data >= 0) | |
260 | return (data); | |
261 | s = socket(AF_INET, SOCK_STREAM, 0); | |
262 | if (s < 0) | |
263 | return (NOTOK); | |
264 | #ifndef BSD43 | |
265 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) 0, 0) < 0) | |
266 | #else | |
267 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) < 0) | |
268 | #endif | |
269 | goto bad; | |
270 | /* anchor socket to avoid multi-homing problems */ | |
271 | data_source.sin_family = AF_INET; | |
272 | data_source.sin_addr = ctrl_addr.sin_addr; | |
273 | if (bind(s, (struct sockaddr *) &data_source, sizeof (data_source)) < 0) | |
274 | goto bad; | |
275 | return (s); | |
276 | bad: | |
277 | (void)close(s); | |
278 | return (NOTOK); | |
279 | } | |
280 | ||
281 | int | |
282 | dataconn(name) | |
283 | char *name; | |
284 | { | |
285 | /* UCB data connection routine */ | |
286 | int retry = 0; | |
287 | ||
288 | if (data >= 0) { | |
289 | reply(125, "Using existing data connection for %s.", | |
290 | name); | |
291 | usedefault = 1; | |
292 | return (data); | |
293 | } | |
294 | if (usedefault) | |
295 | data_dest = his_addr; | |
296 | usedefault = 1; | |
297 | data = getdatasock(); | |
298 | if (data == NOTOK){ | |
299 | reply(425, "Can't create data socket (%s,%d): %s.", | |
300 | inet_ntoa(data_source.sin_addr), | |
301 | ntohs(data_source.sin_port), | |
302 | sys_errlist[errno]); | |
303 | return (NOTOK); | |
304 | } | |
305 | reply(150, "Opening data connection for %s (%s,%d).", | |
306 | name, inet_ntoa(data_dest.sin_addr), | |
307 | ntohs(data_dest.sin_port)); | |
308 | while (connect(data, (struct sockaddr *)&data_dest, sizeof (data_dest)) < 0) { | |
309 | if (errno == EADDRINUSE && retry < swaitmax) { | |
310 | sleep((unsigned) swaitint); | |
311 | retry += swaitint; | |
312 | continue; | |
313 | } | |
314 | reply(425, "Can't build data connection: %s.", | |
315 | sys_errlist[errno]); | |
316 | (void) close(data); | |
317 | data = -1; | |
318 | return (NOTOK); | |
319 | } | |
320 | return (data); | |
321 | } | |
322 | ||
323 | fatal(s) | |
324 | char *s; | |
325 | { | |
326 | reply(451, "Error in server: %s\n", s); | |
327 | /* reply(221, "Closing connection due to server error.");*/ | |
328 | dologout(0); | |
329 | } | |
330 | ||
331 | #ifndef lint | |
332 | reply(va_alist) | |
333 | va_dcl | |
334 | { | |
335 | int n; | |
336 | va_list ap; | |
337 | ||
338 | va_start (ap); | |
339 | ||
340 | n = va_arg (ap, int); | |
341 | ||
342 | _reply (n, ' ', ap); | |
343 | ||
344 | va_end (ap); | |
345 | } | |
346 | ||
347 | lreply(va_alist) | |
348 | va_dcl | |
349 | { | |
350 | int n; | |
351 | va_list ap; | |
352 | ||
353 | va_start (ap); | |
354 | ||
355 | n = va_arg (ap, int); | |
356 | ||
357 | _reply (n, '-', ap); | |
358 | ||
359 | va_end (ap); | |
360 | } | |
361 | ||
362 | static _reply (n, c, ap) | |
363 | int n; | |
364 | char c; | |
365 | va_list ap; | |
366 | { | |
367 | char buffer[BUFSIZ]; | |
368 | ||
369 | _asprintf (buffer, NULLCP, ap); | |
370 | ||
371 | printf ("%d%c%s\r\n", n, c, buffer); | |
372 | (void)fflush (stdout); | |
373 | ||
374 | if (verbose) | |
375 | advise (NULLCP,"<--- %d%c%s", n, c, buffer); | |
376 | } | |
377 | #else | |
378 | /* VARARGS2 */ | |
379 | ||
380 | reply(n,fmt) | |
381 | int n; | |
382 | char *fmt; | |
383 | { | |
384 | reply(n,fmt); | |
385 | } | |
386 | /* VARARGS2 */ | |
387 | ||
388 | lreply(n,fmt) | |
389 | int n; | |
390 | char *fmt; | |
391 | { | |
392 | lreply(n,fmt); | |
393 | } | |
394 | #endif | |
395 | ||
396 | replystr(s) | |
397 | char *s; | |
398 | { | |
399 | printf("%s\r\n", s); | |
400 | (void)fflush(stdout); | |
401 | if (verbose) | |
402 | advise(NULLCP,"<--- %s", s); | |
403 | } | |
404 | ||
405 | ack(s) | |
406 | char *s; | |
407 | { | |
408 | reply(200, "%s command okay.", s); | |
409 | } | |
410 | ||
411 | nack(s) | |
412 | char *s; | |
413 | { | |
414 | reply(502, "%s command not implemented.", s); | |
415 | } | |
416 | ||
417 | /*ARGSUSED*/ | |
418 | yyerror(s) | |
419 | char *s; | |
420 | { | |
421 | reply(500, "Command not understood."); | |
422 | } | |
423 | ||
424 | ftp_delete(name) | |
425 | char *name; | |
426 | { | |
427 | /* f_rm is the general purpose FTAM file/directory deletion routine. | |
428 | * Change information is formatted in ftam_error. | |
429 | */ | |
430 | vec[0] = "f_rm"; | |
431 | vec[1] = name; | |
432 | vec[2] = NULL; | |
433 | ||
434 | if (f_rm(vec) == NOTOK){ | |
435 | reply(550, "%s: %s.", name, ftam_error); | |
436 | return; | |
437 | } | |
438 | ack("DELE"); | |
439 | } | |
440 | ||
441 | makedir(name) | |
442 | char *name; | |
443 | { | |
444 | ||
445 | /* f_mkdir is the FTAM directory creation routine */ | |
446 | ||
447 | vec[0] = "f_mkdir"; | |
448 | vec[1] = name; | |
449 | vec[2] = NULL; | |
450 | ||
451 | if (f_mkdir(vec) == NOTOK){ | |
452 | reply(550, "%s: %s.", name, ftam_error); | |
453 | return; | |
454 | } | |
455 | ack("MKDIR"); | |
456 | } | |
457 | ||
458 | removedir(name) | |
459 | char *name; | |
460 | { | |
461 | ||
462 | /* f_rm is the general purpose FTAM file/directory deletion routine. | |
463 | */ | |
464 | vec[0] = "f_rm"; | |
465 | vec[1] = name; | |
466 | vec[2] = NULL; | |
467 | ||
468 | if (f_rm(vec) == NOTOK){ | |
469 | reply(550, "%s: %s.", name, ftam_error); | |
470 | return; | |
471 | } | |
472 | ack("RMDIR"); | |
473 | } | |
474 | ||
475 | char * | |
476 | renamefrom(name) | |
477 | char *name; | |
478 | { | |
479 | reply(350, "Ready for destination name"); | |
480 | return (name); | |
481 | } | |
482 | ||
483 | renamecmd(from, to) | |
484 | char *from, *to; | |
485 | { | |
486 | ||
487 | /* f_mv is FTAM block function to select and change attributes | |
488 | * (i.e. file name) | |
489 | */ | |
490 | vec[0] ="f_mv"; | |
491 | vec[1] = from; | |
492 | vec[2] = to; | |
493 | vec[3] = NULL; | |
494 | ||
495 | if (f_mv(vec) == NOTOK){ | |
496 | reply(550, "rename: %s.", ftam_error); | |
497 | return; | |
498 | } | |
499 | ack("RNTO"); | |
500 | } | |
501 | ||
502 | dolog(sin) | |
503 | struct sockaddr_in *sin; | |
504 | { | |
505 | #ifdef notanymore | |
506 | struct hostent *hp = gethostbyaddr((char*)&sin->sin_addr, | |
507 | sizeof (struct in_addr), AF_INET); | |
508 | #endif | |
509 | time_t t; | |
510 | ||
511 | #ifdef notanymore | |
512 | if (hp) { | |
513 | (void)strncpy(remotehost, hp->h_name, sizeof (remotehost)); | |
514 | endhostent(); | |
515 | } else | |
516 | #endif | |
517 | (void)strncpy(remotehost, inet_ntoa(sin->sin_addr), | |
518 | sizeof (remotehost)); | |
519 | t = time((time_t*)0); | |
520 | if (!logging) | |
521 | return; | |
522 | advise(NULLCP,"connection from %s at %s", remotehost, ctime(&t)); | |
523 | } | |
524 | directory(how,name) | |
525 | char *how, *name; | |
526 | { | |
527 | ||
528 | int result; | |
529 | /* f_ls does a directory contents transfer. The first arguement | |
530 | * determines whether a name list (NLST) or long list (LIST) is returned. | |
531 | * Results: | |
532 | * OK -- list transfered without error | |
533 | * NOTOK -- list transfer error | |
534 | * DONE -- Problem opening TCP connection for transfer | |
535 | * Error response made by dataconn routine. | |
536 | */ | |
537 | ||
538 | vec[0] = strcmp(how,"NLST") ? "dir" : "ls"; | |
539 | vec[1] = name; | |
540 | vec[2] = NULL; | |
541 | ||
542 | if ((result = f_ls(vec)) == OK) | |
543 | reply(226, "Transfer complete."); | |
544 | else if (result == NOTOK) | |
545 | reply(500, ftam_error); | |
546 | data = -1; | |
547 | ||
548 | } | |
549 | /* | |
550 | * Execute FTAM login if all necessary arguements present | |
551 | */ | |
552 | dologin() | |
553 | { | |
554 | ||
555 | if (!ftp_user) { | |
556 | reply(500,"Send USER command first"); | |
557 | return(0); | |
558 | } | |
559 | if (!ftp_passwd) { | |
560 | reply(330,"Send PASS command"); | |
561 | return(0); | |
562 | } | |
563 | if (!osi_host){ | |
564 | /* Success is returned since most user FTP response scanners | |
565 | * are not prepared for a continue at this point. The osi | |
566 | * host may be specified by encoding it with the user name | |
567 | * (i.e. user@osihost) or using the SITE command (SITE osihost). | |
568 | * Gateway users are expected to know if the remote site | |
569 | * requires account charging information. The bridge makes | |
570 | * ACCT optional. | |
571 | */ | |
572 | reply(200,"Specify OSI filestore with SITE command"); | |
573 | return(0); | |
574 | } | |
575 | vec[0] = "f_open"; | |
576 | vec[1] = osi_host; | |
577 | vec[2] = ftp_user; | |
578 | vec[3] = (ftp_account) ? "" : ftp_account; | |
579 | vec[4] = ftp_passwd; | |
580 | ||
581 | advise (NULLCP, | |
582 | "attempting association with OSI filestore \"%s\" as initiator \"%s\"", | |
583 | osi_host, ftp_user); | |
584 | ||
585 | /* f_open performs the FTAM initialization (including login) */ | |
586 | if (f_open(vec) == NOTOK){ | |
587 | reply(500,"Login failed"); | |
588 | return(0); | |
589 | } | |
590 | ||
591 | reply(200,"Associated with OSI filestore %s", osi_host); | |
592 | return(1); | |
593 | ||
594 | } | |
595 | ||
596 | /* | |
597 | * exit with supplied status. | |
598 | */ | |
599 | dologout(status) | |
600 | int status; | |
601 | { | |
602 | ||
603 | vec[0] = "f_close"; | |
604 | vec[1] = NULL; | |
605 | /* f_close performs the logout sequence and receives charging | |
606 | * information | |
607 | */ | |
608 | (void) f_close(vec); | |
609 | if (status>=0) | |
610 | reply(221,"Logged off. %s",ftam_error); | |
611 | /* beware of flushing buffers after a SIGPIPE */ | |
612 | _exit(status); | |
613 | } | |
614 | ||
615 | ||
616 | /* | |
617 | * Check user requesting login priviledges. | |
618 | * Disallow anyone mentioned in the file FTPUSERS | |
619 | * to allow people such as uucp to be avoided. | |
620 | */ | |
621 | checkuser(name) | |
622 | register char *name; | |
623 | { | |
624 | char line[BUFSIZ], *index(); | |
625 | FILE *fd, *fopen(); | |
626 | int found = 0; | |
627 | ||
628 | fd = fopen(FTPUSERS, "r"); | |
629 | if (fd == NULL) | |
630 | return (1); | |
631 | while (fgets(line, sizeof (line), fd) != NULL) { | |
632 | register char *cp = index(line, '\n'); | |
633 | ||
634 | if (cp) | |
635 | *cp = '\0'; | |
636 | if (strcmp(line, name) == 0) { | |
637 | found++; | |
638 | break; | |
639 | } | |
640 | } | |
641 | (void)fclose(fd); | |
642 | return (!found); | |
643 | } | |
644 |