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