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