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