Status field is now Comment field
[unix-history] / usr / src / libexec / ftpd / ftpd.c
CommitLineData
f644bb55 1/*
07fffe50 2 * Copyright (c) 1985 Regents of the University of California.
f644bb55
DF
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
07fffe50 9"@(#) Copyright (c) 1985 Regents of the University of California.\n\
f644bb55
DF
10 All rights reserved.\n";
11#endif not lint
12
c6d3d85f 13#ifndef lint
c0785a4d 14static char sccsid[] = "@(#)ftpd.c 5.9 (Berkeley) %G%";
f644bb55 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>
07fffe50 31#include <arpa/telnet.h>
23eeaca6 32
c6d3d85f
SL
33#include <stdio.h>
34#include <signal.h>
c6d3d85f
SL
35#include <pwd.h>
36#include <setjmp.h>
37#include <netdb.h>
28e19fb7 38#include <errno.h>
07fffe50 39#include <strings.h>
df2f99b4 40#include <syslog.h>
c6d3d85f 41
0597ed04
SL
42/*
43 * File containing login names
44 * NOT to be used on this machine.
45 * Commonly used to disallow uucp.
46 */
47#define FTPUSERS "/etc/ftpusers"
48
c6d3d85f
SL
49extern int errno;
50extern char *sys_errlist[];
51extern char *crypt();
52extern char version[];
53extern char *home; /* pointer to home directory for glob */
07fffe50 54extern FILE *popen(), *fopen(), *freopen();
df2f99b4 55extern int pclose(), fclose();
07fffe50
GM
56extern char *getline();
57extern char cbuf[];
c6d3d85f
SL
58
59struct sockaddr_in ctrl_addr;
60struct sockaddr_in data_source;
61struct sockaddr_in data_dest;
62struct sockaddr_in his_addr;
63
c6d3d85f 64int data;
07fffe50 65jmp_buf errcatch, urgcatch;
c6d3d85f
SL
66int logged_in;
67struct passwd *pw;
68int debug;
df2f99b4 69int timeout = 900; /* timeout after 15 minutes of inactivity */
9072bd8a 70int logging;
c6d3d85f 71int guest;
5c50e19b 72int wtmp;
c6d3d85f
SL
73int type;
74int form;
75int stru; /* avoid C keyword */
76int mode;
8643b66e 77int usedefault = 1; /* for data transfers */
07fffe50
GM
78int pdata; /* for passive mode */
79int unique;
80int transflag;
81char tmpline[7];
c6d3d85f 82char hostname[32];
bb16805b 83char remotehost[32];
c6d3d85f 84
5ac6fc46
SL
85/*
86 * Timeout intervals for retrying connections
87 * to hosts that don't accept PORT cmds. This
88 * is a kludge, but given the problems with TCP...
89 */
90#define SWAITMAX 90 /* wait at most 90 seconds */
91#define SWAITINT 5 /* interval between retries */
92
93int swaitmax = SWAITMAX;
94int swaitint = SWAITINT;
95
c6d3d85f 96int lostconn();
07fffe50 97int myoob();
c6d3d85f 98FILE *getdatasock(), *dataconn();
c6d3d85f
SL
99
100main(argc, argv)
101 int argc;
102 char *argv[];
103{
d0604b39 104 int addrlen, on = 1;
07fffe50 105 long pgid;
c6d3d85f
SL
106 char *cp;
107
a3dc4d0e
MK
108 addrlen = sizeof (his_addr);
109 if (getpeername(0, &his_addr, &addrlen) < 0) {
df2f99b4 110 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
c6d3d85f
SL
111 exit(1);
112 }
a3dc4d0e 113 addrlen = sizeof (ctrl_addr);
df2f99b4
GM
114 if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) {
115 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
a3dc4d0e
MK
116 exit(1);
117 }
118 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
c6d3d85f 119 debug = 0;
df2f99b4 120 openlog("ftpd", LOG_PID, LOG_DAEMON);
c6d3d85f
SL
121 argc--, argv++;
122 while (argc > 0 && *argv[0] == '-') {
123 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
124
5ac6fc46
SL
125 case 'v':
126 debug = 1;
127 break;
128
c6d3d85f
SL
129 case 'd':
130 debug = 1;
c6d3d85f
SL
131 break;
132
9072bd8a
SL
133 case 'l':
134 logging = 1;
135 break;
136
5ac6fc46
SL
137 case 't':
138 timeout = atoi(++cp);
139 goto nextopt;
140 break;
141
c6d3d85f 142 default:
a3dc4d0e
MK
143 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
144 *cp);
c6d3d85f
SL
145 break;
146 }
5ac6fc46 147nextopt:
c6d3d85f
SL
148 argc--, argv++;
149 }
c0785a4d 150 (void) freopen("/dev/null", "w", stderr);
df2f99b4
GM
151 (void) signal(SIGPIPE, lostconn);
152 (void) signal(SIGCHLD, SIG_IGN);
07fffe50 153 if (signal(SIGURG, myoob) < 0) {
df2f99b4 154 syslog(LOG_ERR, "signal: %m");
07fffe50 155 }
d0604b39
GM
156 /* handle urgent data inline */
157#ifdef SO_OOBINLINE
158 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) {
159 syslog(LOG_ERR, "setsockopt: %m");
160 }
161#endif SO_OOBINLINE
07fffe50 162 pgid = getpid();
d0604b39 163 if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) {
df2f99b4 164 syslog(LOG_ERR, "ioctl: %m");
07fffe50 165 }
2584c5be 166 dolog(&his_addr);
a3dc4d0e
MK
167 /* do telnet option negotiation here */
168 /*
169 * Set up default state
170 */
171 logged_in = 0;
172 data = -1;
173 type = TYPE_A;
174 form = FORM_N;
175 stru = STRU_F;
176 mode = MODE_S;
07fffe50 177 tmpline[0] = '\0';
df2f99b4 178 (void) gethostname(hostname, sizeof (hostname));
a3dc4d0e
MK
179 reply(220, "%s FTP server (%s) ready.",
180 hostname, version);
c6d3d85f 181 for (;;) {
df2f99b4
GM
182 (void) setjmp(errcatch);
183 (void) yyparse();
c6d3d85f
SL
184 }
185}
3cb22fa5 186
c6d3d85f
SL
187lostconn()
188{
189
407329f7 190 if (debug)
df2f99b4 191 syslog(LOG_DEBUG, "lost connection");
407329f7 192 dologout(-1);
c6d3d85f
SL
193}
194
195pass(passwd)
196 char *passwd;
197{
aa159ba8
SL
198 char *xpasswd, *savestr();
199 static struct passwd save;
c6d3d85f
SL
200
201 if (logged_in || pw == NULL) {
202 reply(503, "Login with USER first.");
203 return;
204 }
205 if (!guest) { /* "ftp" is only account allowed no password */
206 xpasswd = crypt(passwd, pw->pw_passwd);
2584c5be
JL
207 /* The strcmp does not catch null passwords! */
208 if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
c6d3d85f
SL
209 reply(530, "Login incorrect.");
210 pw = NULL;
211 return;
212 }
213 }
aa159ba8 214 setegid(pw->pw_gid);
c6d3d85f
SL
215 initgroups(pw->pw_name, pw->pw_gid);
216 if (chdir(pw->pw_dir)) {
7ccaf1e3 217 reply(530, "User %s: can't change directory to %s.",
c6d3d85f 218 pw->pw_name, pw->pw_dir);
aa159ba8 219 goto bad;
c6d3d85f 220 }
5c50e19b 221
2584c5be
JL
222 /* grab wtmp before chroot */
223 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
aa159ba8 224 if (guest && chroot(pw->pw_dir) < 0) {
c6d3d85f 225 reply(550, "Can't set guest privileges.");
2584c5be
JL
226 if (wtmp >= 0) {
227 (void) close(wtmp);
228 wtmp = -1;
229 }
aa159ba8 230 goto bad;
c6d3d85f
SL
231 }
232 if (!guest)
233 reply(230, "User %s logged in.", pw->pw_name);
234 else
235 reply(230, "Guest login ok, access restrictions apply.");
236 logged_in = 1;
bb16805b 237 dologin(pw);
aa159ba8
SL
238 seteuid(pw->pw_uid);
239 /*
240 * Save everything so globbing doesn't
241 * clobber the fields.
242 */
243 save = *pw;
244 save.pw_name = savestr(pw->pw_name);
245 save.pw_passwd = savestr(pw->pw_passwd);
246 save.pw_comment = savestr(pw->pw_comment);
df2f99b4 247 save.pw_gecos = savestr(pw->pw_gecos);
aa159ba8
SL
248 save.pw_dir = savestr(pw->pw_dir);
249 save.pw_shell = savestr(pw->pw_shell);
250 pw = &save;
251 home = pw->pw_dir; /* home dir for globbing */
252 return;
253bad:
254 seteuid(0);
255 pw = NULL;
256}
257
258char *
259savestr(s)
260 char *s;
261{
262 char *malloc();
df2f99b4 263 char *new = malloc((unsigned) strlen(s) + 1);
aa159ba8
SL
264
265 if (new != NULL)
df2f99b4 266 (void) strcpy(new, s);
df2da992 267 return (new);
c6d3d85f
SL
268}
269
270retrieve(cmd, name)
271 char *cmd, *name;
272{
273 FILE *fin, *dout;
274 struct stat st;
07fffe50 275 int (*closefunc)(), tmp;
c6d3d85f
SL
276
277 if (cmd == 0) {
6fcbb8a1
SL
278#ifdef notdef
279 /* no remote command execution -- it's a security hole */
5ac6fc46 280 if (*name == '|')
c6d3d85f
SL
281 fin = popen(name + 1, "r"), closefunc = pclose;
282 else
6fcbb8a1 283#endif
c6d3d85f
SL
284 fin = fopen(name, "r"), closefunc = fclose;
285 } else {
286 char line[BUFSIZ];
287
df2f99b4 288 (void) sprintf(line, cmd, name), name = line;
c6d3d85f
SL
289 fin = popen(line, "r"), closefunc = pclose;
290 }
291 if (fin == NULL) {
edcef58b
SL
292 if (errno != 0)
293 reply(550, "%s: %s.", name, sys_errlist[errno]);
c6d3d85f
SL
294 return;
295 }
296 st.st_size = 0;
297 if (cmd == 0 &&
298 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
299 reply(550, "%s: not a plain file.", name);
300 goto done;
301 }
302 dout = dataconn(name, st.st_size, "w");
303 if (dout == NULL)
304 goto done;
07fffe50 305 if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
c6d3d85f 306 reply(550, "%s: %s.", name, sys_errlist[errno]);
07fffe50
GM
307 }
308 else if (tmp == 0) {
c6d3d85f 309 reply(226, "Transfer complete.");
07fffe50 310 }
df2f99b4 311 (void) fclose(dout);
07fffe50
GM
312 data = -1;
313 pdata = -1;
c6d3d85f
SL
314done:
315 (*closefunc)(fin);
316}
317
318store(name, mode)
319 char *name, *mode;
320{
321 FILE *fout, *din;
07fffe50
GM
322 int (*closefunc)(), dochown = 0, tmp;
323 char *gunique(), *local;
c6d3d85f 324
6fcbb8a1
SL
325#ifdef notdef
326 /* no remote command execution -- it's a security hole */
5ac6fc46 327 if (name[0] == '|')
c6d3d85f 328 fout = popen(&name[1], "w"), closefunc = pclose;
6fcbb8a1
SL
329 else
330#endif
331 {
aa159ba8
SL
332 struct stat st;
333
07fffe50
GM
334 local = name;
335 if (stat(name, &st) < 0) {
aa159ba8 336 dochown++;
07fffe50
GM
337 }
338 else if (unique) {
339 if ((local = gunique(name)) == NULL) {
340 return;
341 }
342 dochown++;
343 }
344 fout = fopen(local, mode), closefunc = fclose;
aa159ba8 345 }
c6d3d85f 346 if (fout == NULL) {
7ccaf1e3 347 reply(553, "%s: %s.", local, sys_errlist[errno]);
c6d3d85f
SL
348 return;
349 }
07fffe50 350 din = dataconn(local, (off_t)-1, "r");
c6d3d85f
SL
351 if (din == NULL)
352 goto done;
07fffe50 353 if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {
7ccaf1e3 354 reply(552, "%s: %s.", local, sys_errlist[errno]);
07fffe50
GM
355 }
356 else if (tmp == 0 && !unique) {
c6d3d85f 357 reply(226, "Transfer complete.");
07fffe50
GM
358 }
359 else if (tmp == 0 && unique) {
360 reply(226, "Transfer complete (unique file name:%s).", local);
361 }
df2f99b4 362 (void) fclose(din);
07fffe50
GM
363 data = -1;
364 pdata = -1;
c6d3d85f 365done:
aa159ba8 366 if (dochown)
07fffe50 367 (void) chown(local, pw->pw_uid, -1);
c6d3d85f
SL
368 (*closefunc)(fout);
369}
370
371FILE *
372getdatasock(mode)
373 char *mode;
374{
3083a381 375 int s, on = 1;
c6d3d85f
SL
376
377 if (data >= 0)
378 return (fdopen(data, mode));
bb16805b 379 s = socket(AF_INET, SOCK_STREAM, 0);
58846728 380 if (s < 0)
c6d3d85f 381 return (NULL);
c6d3d85f 382 seteuid(0);
df2f99b4 383 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
58846728 384 goto bad;
edcef58b
SL
385 /* anchor socket to avoid multi-homing problems */
386 data_source.sin_family = AF_INET;
387 data_source.sin_addr = ctrl_addr.sin_addr;
df2f99b4 388 if (bind(s, &data_source, sizeof (data_source)) < 0)
58846728 389 goto bad;
0a11c3ae 390 seteuid(pw->pw_uid);
c6d3d85f 391 return (fdopen(s, mode));
58846728
SL
392bad:
393 seteuid(pw->pw_uid);
df2f99b4 394 (void) close(s);
58846728 395 return (NULL);
c6d3d85f
SL
396}
397
398FILE *
399dataconn(name, size, mode)
400 char *name;
5ac6fc46 401 off_t size;
c6d3d85f
SL
402 char *mode;
403{
404 char sizebuf[32];
405 FILE *file;
5ac6fc46 406 int retry = 0;
c6d3d85f
SL
407
408 if (size >= 0)
df2f99b4 409 (void) sprintf (sizebuf, " (%ld bytes)", size);
c6d3d85f
SL
410 else
411 (void) strcpy(sizebuf, "");
07fffe50
GM
412 if (pdata > 0) {
413 struct sockaddr_in from;
414 int s, fromlen = sizeof(from);
415
df2f99b4 416 s = accept(pdata, &from, &fromlen);
07fffe50
GM
417 if (s < 0) {
418 reply(425, "Can't open data connection.");
419 (void) close(pdata);
420 pdata = -1;
421 return(NULL);
422 }
423 (void) close(pdata);
424 pdata = s;
425 reply(150, "Openning data connection for %s (%s,%d)%s.",
df2f99b4 426 name, inet_ntoa(from.sin_addr),
07fffe50
GM
427 ntohs(from.sin_port), sizebuf);
428 return(fdopen(pdata, mode));
429 }
c6d3d85f
SL
430 if (data >= 0) {
431 reply(125, "Using existing data connection for %s%s.",
432 name, sizebuf);
8643b66e 433 usedefault = 1;
c6d3d85f
SL
434 return (fdopen(data, mode));
435 }
afa6f79b 436 if (usedefault)
5c81b68e 437 data_dest = his_addr;
5c81b68e 438 usedefault = 1;
c6d3d85f
SL
439 file = getdatasock(mode);
440 if (file == NULL) {
441 reply(425, "Can't create data socket (%s,%d): %s.",
bb16805b 442 inet_ntoa(data_source.sin_addr),
c6d3d85f
SL
443 ntohs(data_source.sin_port),
444 sys_errlist[errno]);
445 return (NULL);
446 }
447 data = fileno(file);
07fffe50 448 while (connect(data, &data_dest, sizeof (data_dest)) < 0) {
5ac6fc46 449 if (errno == EADDRINUSE && retry < swaitmax) {
df2f99b4 450 sleep((unsigned) swaitint);
5ac6fc46
SL
451 retry += swaitint;
452 continue;
453 }
c6d3d85f
SL
454 reply(425, "Can't build data connection: %s.",
455 sys_errlist[errno]);
456 (void) fclose(file);
457 data = -1;
458 return (NULL);
459 }
d0604b39
GM
460 reply(150, "Opening data connection for %s (%s,%d)%s.",
461 name, inet_ntoa(data_dest.sin_addr),
462 ntohs(data_dest.sin_port), sizebuf);
c6d3d85f
SL
463 return (file);
464}
465
466/*
467 * Tranfer the contents of "instr" to
468 * "outstr" peer using the appropriate
469 * encapulation of the date subject
470 * to Mode, Structure, and Type.
471 *
472 * NB: Form isn't handled.
473 */
474send_data(instr, outstr)
475 FILE *instr, *outstr;
476{
477 register int c;
478 int netfd, filefd, cnt;
479 char buf[BUFSIZ];
480
07fffe50
GM
481 transflag++;
482 if (setjmp(urgcatch)) {
483 transflag = 0;
484 return(-1);
485 }
c6d3d85f
SL
486 switch (type) {
487
488 case TYPE_A:
489 while ((c = getc(instr)) != EOF) {
d33c618b 490 if (c == '\n') {
07fffe50
GM
491 if (ferror (outstr)) {
492 transflag = 0;
d33c618b 493 return (1);
07fffe50 494 }
d0604b39 495 (void) putc('\r', outstr);
d33c618b 496 }
d0604b39 497 (void) putc(c, outstr);
07fffe50
GM
498 /* if (c == '\r') */
499 /* putc ('\0', outstr); */
c6d3d85f 500 }
07fffe50
GM
501 transflag = 0;
502 if (ferror (instr) || ferror (outstr)) {
d33c618b 503 return (1);
07fffe50 504 }
c6d3d85f
SL
505 return (0);
506
507 case TYPE_I:
508 case TYPE_L:
509 netfd = fileno(outstr);
510 filefd = fileno(instr);
511
07fffe50
GM
512 while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
513 if (write(netfd, buf, cnt) < 0) {
514 transflag = 0;
c6d3d85f 515 return (1);
07fffe50
GM
516 }
517 }
518 transflag = 0;
c6d3d85f
SL
519 return (cnt < 0);
520 }
7ccaf1e3 521 reply(550, "Unimplemented TYPE %d in send_data", type);
07fffe50 522 transflag = 0;
7ccaf1e3 523 return (-1);
c6d3d85f
SL
524}
525
526/*
527 * Transfer data from peer to
528 * "outstr" using the appropriate
529 * encapulation of the data subject
530 * to Mode, Structure, and Type.
531 *
532 * N.B.: Form isn't handled.
533 */
534receive_data(instr, outstr)
535 FILE *instr, *outstr;
536{
537 register int c;
d33c618b 538 int cnt;
c6d3d85f
SL
539 char buf[BUFSIZ];
540
541
07fffe50
GM
542 transflag++;
543 if (setjmp(urgcatch)) {
544 transflag = 0;
545 return(-1);
546 }
c6d3d85f
SL
547 switch (type) {
548
549 case TYPE_I:
550 case TYPE_L:
07fffe50
GM
551 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
552 if (write(fileno(outstr), buf, cnt) < 0) {
553 transflag = 0;
c6d3d85f 554 return (1);
07fffe50
GM
555 }
556 }
557 transflag = 0;
c6d3d85f
SL
558 return (cnt < 0);
559
560 case TYPE_E:
7ccaf1e3 561 reply(553, "TYPE E not implemented.");
07fffe50 562 transflag = 0;
7ccaf1e3 563 return (-1);
c6d3d85f
SL
564
565 case TYPE_A:
c6d3d85f 566 while ((c = getc(instr)) != EOF) {
d0604b39 567 while (c == '\r') {
07fffe50
GM
568 if (ferror (outstr)) {
569 transflag = 0;
d33c618b 570 return (1);
07fffe50 571 }
d33c618b 572 if ((c = getc(instr)) != '\n')
d0604b39 573 (void) putc ('\r', outstr);
07fffe50
GM
574 /* if (c == '\0') */
575 /* continue; */
c6d3d85f 576 }
d0604b39 577 (void) putc (c, outstr);
c6d3d85f 578 }
07fffe50 579 transflag = 0;
d33c618b
SL
580 if (ferror (instr) || ferror (outstr))
581 return (1);
c6d3d85f
SL
582 return (0);
583 }
07fffe50 584 transflag = 0;
c6d3d85f
SL
585 fatal("Unknown type in receive_data.");
586 /*NOTREACHED*/
587}
588
589fatal(s)
590 char *s;
591{
592 reply(451, "Error in server: %s\n", s);
593 reply(221, "Closing connection due to server error.");
bb16805b 594 dologout(0);
c6d3d85f
SL
595}
596
df2f99b4 597/*VARARGS2*/
c6d3d85f
SL
598reply(n, s, args)
599 int n;
600 char *s;
601{
602
603 printf("%d ", n);
604 _doprnt(s, &args, stdout);
605 printf("\r\n");
df2f99b4 606 (void) fflush(stdout);
c6d3d85f 607 if (debug) {
df2f99b4
GM
608 syslog(LOG_DEBUG, "<--- %d ", n);
609 syslog(LOG_DEBUG, s, &args);
c6d3d85f
SL
610 }
611}
612
df2f99b4 613/*VARARGS2*/
c6d3d85f
SL
614lreply(n, s, args)
615 int n;
616 char *s;
617{
618 printf("%d-", n);
619 _doprnt(s, &args, stdout);
620 printf("\r\n");
df2f99b4 621 (void) fflush(stdout);
c6d3d85f 622 if (debug) {
df2f99b4
GM
623 syslog(LOG_DEBUG, "<--- %d- ", n);
624 syslog(LOG_DEBUG, s, &args);
c6d3d85f
SL
625 }
626}
627
c6d3d85f
SL
628ack(s)
629 char *s;
630{
7ccaf1e3 631 reply(250, "%s command successful.", s);
c6d3d85f
SL
632}
633
634nack(s)
635 char *s;
636{
637 reply(502, "%s command not implemented.", s);
638}
639
df2f99b4
GM
640yyerror(s)
641 char *s;
c6d3d85f 642{
07fffe50
GM
643 char *cp;
644
645 cp = index(cbuf,'\n');
646 *cp = '\0';
647 reply(500, "'%s': command not understood.",cbuf);
c6d3d85f
SL
648}
649
650delete(name)
651 char *name;
652{
653 struct stat st;
654
655 if (stat(name, &st) < 0) {
656 reply(550, "%s: %s.", name, sys_errlist[errno]);
657 return;
658 }
659 if ((st.st_mode&S_IFMT) == S_IFDIR) {
660 if (rmdir(name) < 0) {
661 reply(550, "%s: %s.", name, sys_errlist[errno]);
662 return;
663 }
664 goto done;
665 }
666 if (unlink(name) < 0) {
667 reply(550, "%s: %s.", name, sys_errlist[errno]);
668 return;
669 }
670done:
671 ack("DELE");
672}
673
674cwd(path)
675 char *path;
676{
677
678 if (chdir(path) < 0) {
679 reply(550, "%s: %s.", path, sys_errlist[errno]);
680 return;
681 }
682 ack("CWD");
683}
684
aa159ba8 685makedir(name)
c6d3d85f
SL
686 char *name;
687{
aa159ba8
SL
688 struct stat st;
689 int dochown = stat(name, &st) < 0;
c6d3d85f
SL
690
691 if (mkdir(name, 0777) < 0) {
692 reply(550, "%s: %s.", name, sys_errlist[errno]);
693 return;
694 }
aa159ba8
SL
695 if (dochown)
696 (void) chown(name, pw->pw_uid, -1);
7ccaf1e3 697 reply(257, "MKD command successful.");
c6d3d85f
SL
698}
699
aa159ba8 700removedir(name)
c6d3d85f
SL
701 char *name;
702{
703
704 if (rmdir(name) < 0) {
705 reply(550, "%s: %s.", name, sys_errlist[errno]);
706 return;
707 }
7ccaf1e3 708 ack("RMD");
c6d3d85f
SL
709}
710
aa159ba8 711pwd()
c6d3d85f 712{
aa159ba8 713 char path[MAXPATHLEN + 1];
c6d3d85f
SL
714
715 if (getwd(path) == NULL) {
7ccaf1e3 716 reply(550, "%s.", path);
c6d3d85f
SL
717 return;
718 }
7ccaf1e3 719 reply(257, "\"%s\" is current directory.", path);
c6d3d85f
SL
720}
721
722char *
723renamefrom(name)
724 char *name;
725{
726 struct stat st;
727
728 if (stat(name, &st) < 0) {
729 reply(550, "%s: %s.", name, sys_errlist[errno]);
730 return ((char *)0);
731 }
aa159ba8 732 reply(350, "File exists, ready for destination name");
c6d3d85f
SL
733 return (name);
734}
735
736renamecmd(from, to)
737 char *from, *to;
738{
739
740 if (rename(from, to) < 0) {
741 reply(550, "rename: %s.", sys_errlist[errno]);
742 return;
743 }
744 ack("RNTO");
745}
746
c6d3d85f
SL
747dolog(sin)
748 struct sockaddr_in *sin;
749{
750 struct hostent *hp = gethostbyaddr(&sin->sin_addr,
751 sizeof (struct in_addr), AF_INET);
c6d3d85f 752 time_t t;
df2f99b4 753 extern char *ctime();
c6d3d85f 754
bb16805b 755 if (hp) {
df2f99b4 756 (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
bb16805b
SL
757 endhostent();
758 } else
df2f99b4 759 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
bb16805b
SL
760 sizeof (remotehost));
761 if (!logging)
762 return;
df2f99b4
GM
763 t = time((time_t *) 0);
764 syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t));
c6d3d85f 765}
0597ed04 766
bb16805b
SL
767#include <utmp.h>
768
df2f99b4 769#define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
bb16805b
SL
770struct utmp utmp;
771
772/*
773 * Record login in wtmp file.
774 */
775dologin(pw)
776 struct passwd *pw;
777{
bb16805b
SL
778 char line[32];
779
bb16805b
SL
780 if (wtmp >= 0) {
781 /* hack, but must be unique and no tty line */
df2f99b4 782 (void) sprintf(line, "ftp%d", getpid());
bb16805b
SL
783 SCPYN(utmp.ut_line, line);
784 SCPYN(utmp.ut_name, pw->pw_name);
785 SCPYN(utmp.ut_host, remotehost);
df2f99b4 786 utmp.ut_time = (long) time((time_t *) 0);
bb16805b 787 (void) write(wtmp, (char *)&utmp, sizeof (utmp));
2584c5be
JL
788 if (!guest) { /* anon must hang on */
789 (void) close(wtmp);
790 wtmp = -1;
791 }
bb16805b
SL
792 }
793}
794
795/*
796 * Record logout in wtmp file
797 * and exit with supplied status.
798 */
799dologout(status)
800 int status;
801{
a3dc4d0e 802
632ff766
SL
803 if (logged_in) {
804 (void) seteuid(0);
805 if (wtmp < 0)
806 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
807 if (wtmp >= 0) {
808 SCPYN(utmp.ut_name, "");
809 SCPYN(utmp.ut_host, "");
df2f99b4 810 utmp.ut_time = (long) time((time_t *) 0);
632ff766
SL
811 (void) write(wtmp, (char *)&utmp, sizeof (utmp));
812 (void) close(wtmp);
813 }
bb16805b 814 }
930b478b
SL
815 /* beware of flushing buffers after a SIGPIPE */
816 _exit(status);
bb16805b
SL
817}
818
0597ed04
SL
819/*
820 * Special version of popen which avoids
821 * call to shell. This insures noone may
822 * create a pipe to a hidden program as a side
823 * effect of a list or dir command.
824 */
825#define tst(a,b) (*mode == 'r'? (b) : (a))
826#define RDR 0
827#define WTR 1
828static int popen_pid[5];
829
830static char *
831nextarg(cpp)
832 char *cpp;
833{
834 register char *cp = cpp;
835
836 if (cp == 0)
837 return (cp);
838 while (*cp && *cp != ' ' && *cp != '\t')
839 cp++;
840 if (*cp == ' ' || *cp == '\t') {
841 *cp++ = '\0';
842 while (*cp == ' ' || *cp == '\t')
843 cp++;
844 }
845 if (cp == cpp)
846 return ((char *)0);
847 return (cp);
848}
849
850FILE *
851popen(cmd, mode)
852 char *cmd, *mode;
853{
1668a723 854 int p[2], ac, gac;
0597ed04 855 register myside, hisside, pid;
1668a723 856 char *av[20], *gav[512];
0597ed04
SL
857 register char *cp;
858
859 if (pipe(p) < 0)
860 return (NULL);
861 cp = cmd, ac = 0;
1668a723 862 /* break up string into pieces */
0597ed04
SL
863 do {
864 av[ac++] = cp;
865 cp = nextarg(cp);
1668a723 866 } while (cp && *cp && ac < 20);
0597ed04 867 av[ac] = (char *)0;
1668a723
BJ
868 gav[0] = av[0];
869 /* glob each piece */
870 for (gac = ac = 1; av[ac] != NULL; ac++) {
871 char **pop;
6aff9cb0 872 extern char **glob(), **copyblk();
1668a723
BJ
873
874 pop = glob(av[ac]);
6aff9cb0
SL
875 if (pop == (char **)NULL) { /* globbing failed */
876 char *vv[2];
877
878 vv[0] = av[ac];
879 vv[1] = 0;
880 pop = copyblk(vv);
1668a723 881 }
6aff9cb0
SL
882 av[ac] = (char *)pop; /* save to free later */
883 while (*pop && gac < 512)
884 gav[gac++] = *pop++;
9072bd8a 885 }
1668a723 886 gav[gac] = (char *)0;
0597ed04
SL
887 myside = tst(p[WTR], p[RDR]);
888 hisside = tst(p[RDR], p[WTR]);
889 if ((pid = fork()) == 0) {
890 /* myside and hisside reverse roles in child */
df2f99b4
GM
891 (void) close(myside);
892 (void) dup2(hisside, tst(0, 1));
893 (void) close(hisside);
1668a723 894 execv(gav[0], gav);
0597ed04
SL
895 _exit(1);
896 }
1668a723
BJ
897 for (ac = 1; av[ac] != NULL; ac++)
898 blkfree((char **)av[ac]);
0597ed04
SL
899 if (pid == -1)
900 return (NULL);
901 popen_pid[myside] = pid;
df2f99b4 902 (void) close(hisside);
0597ed04
SL
903 return (fdopen(myside, mode));
904}
905
906pclose(ptr)
907 FILE *ptr;
908{
909 register f, r, (*hstat)(), (*istat)(), (*qstat)();
910 int status;
911
912 f = fileno(ptr);
df2f99b4 913 (void) fclose(ptr);
0597ed04
SL
914 istat = signal(SIGINT, SIG_IGN);
915 qstat = signal(SIGQUIT, SIG_IGN);
916 hstat = signal(SIGHUP, SIG_IGN);
917 while ((r = wait(&status)) != popen_pid[f] && r != -1)
918 ;
919 if (r == -1)
920 status = -1;
df2f99b4
GM
921 (void) signal(SIGINT, istat);
922 (void) signal(SIGQUIT, qstat);
923 (void) signal(SIGHUP, hstat);
0597ed04
SL
924 return (status);
925}
926
927/*
928 * Check user requesting login priviledges.
132d8c0e
KM
929 * Disallow anyone who does not have a standard
930 * shell returned by getusershell() (/etc/shells).
0597ed04
SL
931 * Disallow anyone mentioned in the file FTPUSERS
932 * to allow people such as uucp to be avoided.
933 */
934checkuser(name)
935 register char *name;
936{
132d8c0e
KM
937 register char *cp;
938 char line[BUFSIZ], *index(), *getusershell();
0597ed04 939 FILE *fd;
132d8c0e 940 struct passwd *pw;
0597ed04
SL
941 int found = 0;
942
132d8c0e
KM
943 pw = getpwnam(name);
944 if (pw == NULL)
945 return (0);
4ae615fc
KB
946 if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL)
947 pw->pw_shell = "/bin/sh";
132d8c0e
KM
948 while ((cp = getusershell()) != NULL)
949 if (strcmp(cp, pw->pw_shell) == 0)
950 break;
132d8c0e
KM
951 endusershell();
952 if (cp == NULL)
953 return (0);
0597ed04
SL
954 fd = fopen(FTPUSERS, "r");
955 if (fd == NULL)
956 return (1);
957 while (fgets(line, sizeof (line), fd) != NULL) {
132d8c0e 958 cp = index(line, '\n');
0597ed04
SL
959 if (cp)
960 *cp = '\0';
961 if (strcmp(line, name) == 0) {
962 found++;
963 break;
964 }
965 }
df2f99b4 966 (void) fclose(fd);
0597ed04
SL
967 return (!found);
968}
07fffe50
GM
969
970myoob()
971{
d0604b39 972 char *cp;
07fffe50 973
d0604b39 974 /* only process if transfer occurring */
07fffe50 975 if (!transflag) {
07fffe50
GM
976 return;
977 }
07fffe50 978 cp = tmpline;
d0604b39
GM
979 if (getline(cp, 7, stdin) == NULL) {
980 reply(221, "You could at least say goodby.");
981 dologout(0);
982 }
07fffe50 983 upper(cp);
c50979a2 984 if (strcmp(cp, "ABOR\r\n"))
07fffe50 985 return;
07fffe50
GM
986 tmpline[0] = '\0';
987 reply(426,"Transfer aborted. Data connection closed.");
988 reply(226,"Abort successful");
989 longjmp(urgcatch, 1);
990}
991
7ccaf1e3
KM
992/*
993 * Note: The 530 reply codes could be 4xx codes, except nothing is
994 * given in the state tables except 421 which implies an exit. (RFC959)
995 */
07fffe50
GM
996passive()
997{
998 int len;
999 struct sockaddr_in tmp;
1000 register char *p, *a;
1001
1002 pdata = socket(AF_INET, SOCK_STREAM, 0);
1003 if (pdata < 0) {
7ccaf1e3 1004 reply(530, "Can't open passive connection");
07fffe50
GM
1005 return;
1006 }
1007 tmp = ctrl_addr;
1008 tmp.sin_port = 0;
1009 seteuid(0);
df2f99b4 1010 if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
07fffe50
GM
1011 seteuid(pw->pw_uid);
1012 (void) close(pdata);
1013 pdata = -1;
7ccaf1e3 1014 reply(530, "Can't open passive connection");
07fffe50
GM
1015 return;
1016 }
1017 seteuid(pw->pw_uid);
1018 len = sizeof(tmp);
1019 if (getsockname(pdata, (char *) &tmp, &len) < 0) {
1020 (void) close(pdata);
1021 pdata = -1;
7ccaf1e3 1022 reply(530, "Can't open passive connection");
07fffe50
GM
1023 return;
1024 }
1025 if (listen(pdata, 1) < 0) {
1026 (void) close(pdata);
1027 pdata = -1;
7ccaf1e3 1028 reply(530, "Can't open passive connection");
07fffe50
GM
1029 return;
1030 }
1031 a = (char *) &tmp.sin_addr;
1032 p = (char *) &tmp.sin_port;
1033
1034#define UC(b) (((int) b) & 0xff)
1035
1036 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1037 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1038}
1039
1040char *
1041gunique(local)
1042 char *local;
1043{
1044 static char new[MAXPATHLEN];
1045 char *cp = rindex(local, '/');
1046 int d, count=0;
1047 char ext = '1';
1048
1049 if (cp) {
1050 *cp = '\0';
1051 }
1052 d = access(cp ? local : ".", 2);
1053 if (cp) {
1054 *cp = '/';
1055 }
1056 if (d < 0) {
df2f99b4 1057 syslog(LOG_ERR, "%s: %m", local);
07fffe50
GM
1058 return((char *) 0);
1059 }
1060 (void) strcpy(new, local);
1061 cp = new + strlen(new);
1062 *cp++ = '.';
1063 while (!d) {
1064 if (++count == 100) {
7ccaf1e3 1065 reply(452, "Unique file name not cannot be created.");
07fffe50
GM
1066 return((char *) 0);
1067 }
1068 *cp++ = ext;
1069 *cp = '\0';
1070 if (ext == '9') {
1071 ext = '0';
1072 }
1073 else {
1074 ext++;
1075 }
1076 if ((d = access(new, 0)) < 0) {
1077 break;
1078 }
1079 if (ext != '0') {
1080 cp--;
1081 }
1082 else if (*(cp - 2) == '.') {
1083 *(cp - 1) = '1';
1084 }
1085 else {
1086 *(cp - 2) = *(cp - 2) + 1;
1087 cp--;
1088 }
1089 }
1090 return(new);
1091}