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