opieftpd.c revision 29964
1/* opieftpd.c: Main program for an FTP daemon. 2 3%%% portions-copyright-cmetz-96 4Portions of this software are Copyright 1996-1997 by Craig Metz, All Rights 5Reserved. The Inner Net License Version 2 applies to these portions of 6the software. 7You should have received a copy of the license with this software. If 8you didn't get a copy, you may request one from <license@inner.net>. 9 10Portions of this software are Copyright 1995 by Randall Atkinson and Dan 11McDonald, All Rights Reserved. All Rights under this copyright are assigned 12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13License Agreement applies to this software. 14 15 History: 16 17 Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes. 18 Merged in a security fix to BSD-derived ftpds. 19 Modified by cmetz for OPIE 2.3. Fixed the filename at the top. 20 Moved LS_COMMAND here. 21 Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al. 22 Removed useless strings (I don't think that removing the 23 ucb copyright one is a problem -- please let me know if 24 I'm wrong). Changed default CMASK to 077. Removed random 25 comments. Use ANSI stdargs for reply/lreply if we can, 26 added stdargs version of reply/lreply. Don't declare the 27 tos variable unless IP_TOS defined. Include stdargs headers 28 early. More headers ifdefed. Made everything static. 29 Got rid of gethostname() call and use of hostname. Pared 30 down status response for places where header files frequently 31 cause trouble. Made logging of user logins (ala -l) 32 non-optional. Moved reply()/lrepy(). Fixed some prototypes. 33 Modified at NRL for OPIE 2.1. Added declaration of envp. Discard 34 result of opiechallenge (allows access control to work). 35 Added patches for AIX. Symbol changes for autoconf. 36 Modified at NRL for OPIE 2.01. Changed password lookup handling 37 to avoid problems with drain-bamaged shadow password packages. 38 Properly handle internal state for anonymous FTP. Unlock 39 user accounts properly if login fails because of /etc/shells. 40 Make sure to close syslog by function to avoid problems with 41 drain bamaged syslog implementations. 42 Modified at NRL for OPIE 2.0. 43 Originally from BSD Net/2. 44 45 There is some really, really ugly code in here. 46*/ 47/* 48 * Copyright (c) 1985, 1988, 1990 Regents of the University of California. 49 * All rights reserved. 50 * 51 * Redistribution and use in source and binary forms, with or without 52 * modification, are permitted provided that the following conditions 53 * are met: 54 * 1. Redistributions of source code must retain the above copyright 55 * notice, this list of conditions and the following disclaimer. 56 * 2. Redistributions in binary form must reproduce the above copyright 57 * notice, this list of conditions and the following disclaimer in the 58 * documentation and/or other materials provided with the distribution. 59 * 3. All advertising materials mentioning features or use of this software 60 * must display the following acknowledgement: 61 * This product includes software developed by the University of 62 * California, Berkeley and its contributors. 63 * 4. Neither the name of the University nor the names of its contributors 64 * may be used to endorse or promote products derived from this software 65 * without specific prior written permission. 66 * 67 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 68 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 69 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 70 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 71 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 72 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 73 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 74 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 75 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 76 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 77 * SUCH DAMAGE. 78 */ 79 80#include "opie_cfg.h" 81 82#if HAVE_ANSISTDARG 83#include <stdarg.h> 84#endif /* HAVE_ANSISTDARG */ 85 86/* 87 * FTP server. 88 */ 89 90#if HAVE_SYS_PARAM_H 91#include <sys/param.h> 92#endif /* HAVE_SYS_PARAM_H */ 93#include <sys/stat.h> 94/* #include <sys/ioctl.h> */ 95#include <sys/socket.h> 96#include <sys/wait.h> 97#ifdef SYS_FCNTL_H 98#include <sys/fcntl.h> 99#else 100#include <fcntl.h> 101#endif /* SYS_FCNTL_H */ 102#include <sys/types.h> 103 104#include <netinet/in.h> 105#include <netinet/in_systm.h> 106#include <netinet/ip.h> 107 108#define FTP_NAMES 109#include <arpa/ftp.h> 110#include <arpa/inet.h> 111#include <arpa/telnet.h> 112 113#include <signal.h> 114#include <dirent.h> 115#include <fcntl.h> 116#if HAVE_TIME_H 117#include <time.h> 118#endif /* HAVE_TIME_H */ 119#if HAVE_PWD_H 120#include <pwd.h> 121#endif /* HAVE_PWD_H */ 122#include <setjmp.h> 123#include <netdb.h> 124#include <errno.h> 125#include <syslog.h> 126#if HAVE_UNISTD_H 127#include <unistd.h> 128#endif /* HAVE_UNISTD_H */ 129#include <stdio.h> 130#include <ctype.h> 131#include <stdlib.h> 132#include <string.h> 133#include <grp.h> 134 135#include "opie.h" 136 137#if HAVE_SHADOW_H 138#include <shadow.h> 139#endif /* HAVE_SHADOW_H */ 140 141#if HAVE_CRYPT_H 142#include <crypt.h> 143#endif /* HAVE_CRYPT_H */ 144 145#if HAVE_SYS_UTSNAME_H 146#include <sys/utsname.h> 147#endif /* HAVE_SYS_UTSNAME_H */ 148 149#ifdef _AIX 150#include <sys/id.h> 151#include <sys/priv.h> 152#endif /* _AIX */ 153 154#ifdef IP_TOS 155#ifndef IPTOS_THROUGHPUT 156#undef IP_TOS 157#endif /* !IPTOS_THROUGHPUT */ 158#ifndef IPTOS_LOWDELAY 159#undef IP_TOS 160#endif /* !IPTOS_LOWDELAY */ 161#endif /* IP_TOS */ 162 163extern int errno; 164extern char *home; /* pointer to home directory for glob */ 165extern FILE *ftpd_popen __P((char *, char *)); 166extern int ftpd_pclose __P((FILE *)); 167extern char cbuf[]; 168extern off_t restart_point; 169 170static struct sockaddr_in ctrl_addr; 171static struct sockaddr_in data_source; 172struct sockaddr_in data_dest; 173struct sockaddr_in his_addr; 174static struct sockaddr_in pasv_addr; 175 176static int data; 177jmp_buf errcatch; 178static jmp_buf urgcatch; 179int logged_in; 180struct passwd *pw; 181int debug; 182int timeout = 900; /* timeout after 15 minutes of inactivity */ 183int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */ 184 185#if DOANONYMOUS 186static int guest; 187#endif /* DOANONYMOUS */ 188int type; 189int form; 190static int stru; /* avoid C keyword */ 191static int mode; 192int usedefault = 1; /* for data transfers */ 193int pdata = -1; /* for passive mode */ 194static int transflag; 195static off_t file_size; 196static off_t byte_count; 197 198#if (!defined(CMASK) || CMASK == 0) 199#undef CMASK 200#define CMASK 077 201#endif 202 203static int defumask = CMASK; /* default umask value */ 204char tmpline[7]; 205char remotehost[MAXHOSTNAMELEN]; 206 207/* 208 * Timeout intervals for retrying connections 209 * to hosts that don't accept PORT cmds. This 210 * is a kludge, but given the problems with TCP... 211 */ 212#define SWAITMAX 90 /* wait at most 90 seconds */ 213#define SWAITINT 5 /* interval between retries */ 214 215static int swaitmax = SWAITMAX; 216static int swaitint = SWAITINT; 217 218#if DOTITLE 219static char **Argv = NULL; /* pointer to argument vector */ 220static char *LastArgv = NULL; /* end of argv */ 221static char proctitle[BUFSIZ]; /* initial part of title */ 222#endif /* DOTITLE */ 223 224static int af_pwok = 0, pwok = 0; 225static struct opie opiestate; 226 227VOIDRET perror_reply __P((int, char *)); 228VOIDRET dologout __P((int)); 229char *getline __P((char *, int, FILE *)); 230VOIDRET upper __P((char *)); 231 232static VOIDRET lostconn __P((int)); 233static VOIDRET myoob __P((int)); 234static FILE *getdatasock __P((char *)); 235static FILE *dataconn __P((char *, off_t, char *)); 236static int checkuser __P((char *)); 237static VOIDRET end_login __P((void)); 238static VOIDRET send_data __P((FILE *, FILE *, off_t)); 239static int receive_data __P((FILE *, FILE *)); 240static char *gunique __P((char *)); 241static char *sgetsave __P((char *)); 242 243int opielogwtmp __P((char *, char *, char *)); 244 245int fclose __P((FILE *)); 246 247#ifdef HAVE_ANSISTDARG 248VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 249{ 250 va_list ap; 251 char buffer[1024]; 252 253 va_start(ap, fmt); 254 vsprintf(buffer, fmt, ap); 255 va_end(ap); 256 257 printf("%d %s\r\n", n, buffer); 258 fflush(stdout); 259 if (debug) 260 syslog(LOG_DEBUG, "<--- %d %s", n, buffer); 261} 262#else /* HAVE_ANSISTDARG */ 263VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5) 264{ 265 printf("%d ", n); 266 printf(fmt, p0, p1, p2, p3, p4, p5); 267 printf("\r\n"); 268 fflush(stdout); 269 if (debug) { 270 syslog(LOG_DEBUG, "<--- %d ", n); 271 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 272 } 273} 274#endif /* HAVE_ANSISTDARG */ 275 276#ifdef HAVE_ANSISTDARG 277VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 278{ 279 va_list ap; 280 char buffer[1024]; 281 282 va_start(ap, fmt); 283 vsprintf(buffer, fmt, ap); 284 va_end(ap); 285 286 printf("%d- %s\r\n", n, buffer); 287 fflush(stdout); 288 if (debug) 289 syslog(LOG_DEBUG, "<--- %d- %s", n, buffer); 290} 291#else /* HAVE_ANSISTDARG */ 292VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5) 293{ 294 printf("%d- ", n); 295 printf(fmt, p0, p1, p2, p3, p4, p5); 296 printf("\r\n"); 297 fflush(stdout); 298 if (debug) { 299 syslog(LOG_DEBUG, "<--- %d- ", n); 300 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 301 } 302} 303#endif /* HAVE_ANSISTDARG */ 304 305VOIDRET enable_signalling FUNCTION_NOARGS 306{ 307 signal(SIGPIPE, lostconn); 308 if ((int)signal(SIGURG, myoob) < 0) 309 syslog(LOG_ERR, "signal: %m"); 310} 311 312VOIDRET disable_signalling FUNCTION_NOARGS 313{ 314 signal(SIGPIPE, SIG_IGN); 315 if ((int)signal(SIGURG, SIG_IGN) < 0) 316 syslog(LOG_ERR, "signal: %m"); 317} 318 319static VOIDRET lostconn FUNCTION((input), int input) 320{ 321 if (debug) 322 syslog(LOG_DEBUG, "lost connection"); 323 dologout(-1); 324} 325 326static char ttyline[20]; 327 328/* 329 * Helper function for sgetpwnam(). 330 */ 331static char *sgetsave FUNCTION((s), char *s) 332{ 333 char *new = malloc((unsigned) strlen(s) + 1); 334 335 if (new == NULL) { 336 perror_reply(421, "Local resource failure: malloc"); 337 dologout(1); 338 /* NOTREACHED */ 339 } 340 strcpy(new, s); 341 return (new); 342} 343 344/* 345 * Save the result of a getpwnam. Used for USER command, since 346 * the data returned must not be clobbered by any other command 347 * (e.g., globbing). 348 */ 349static struct passwd *sgetpwnam FUNCTION((name), char *name) 350{ 351 static struct passwd save; 352 register struct passwd *p; 353 354#if HAVE_SHADOW 355 struct spwd *spwd; 356#endif /* HAVE_SHADOW */ 357 358 if ((p = getpwnam(name)) == NULL) 359 return (p); 360 361#if HAVE_SHADOW 362 if ((spwd = getspnam(name)) == NULL) 363 return NULL; 364 365 endspent(); 366 367 p->pw_passwd = spwd->sp_pwdp; 368#endif /* HAVE_SHADOW */ 369 370 endpwent(); 371 372 if (save.pw_name) { 373 free(save.pw_name); 374 free(save.pw_passwd); 375 free(save.pw_gecos); 376 free(save.pw_dir); 377 free(save.pw_shell); 378 } 379 save = *p; 380 save.pw_name = sgetsave(p->pw_name); 381 save.pw_passwd = sgetsave(p->pw_passwd); 382 save.pw_gecos = sgetsave(p->pw_gecos); 383 save.pw_dir = sgetsave(p->pw_dir); 384 save.pw_shell = sgetsave(p->pw_shell); 385 return (&save); 386} 387 388int login_attempts; /* number of failed login attempts */ 389int askpasswd; /* had user command, ask for passwd */ 390 391/* 392 * USER command. 393 * Sets global passwd pointer pw if named account exists and is acceptable; 394 * sets askpasswd if a PASS command is expected. If logged in previously, 395 * need to reset state. If name is "ftp" or "anonymous", the name is not in 396 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 397 * If account doesn't exist, ask for passwd anyway. Otherwise, check user 398 * requesting login privileges. Disallow anyone who does not have a standard 399 * shell as returned by getusershell(). Disallow anyone mentioned in the file 400 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 401 */ 402int user FUNCTION((name), char *name) 403{ 404 register char *cp; 405 char *shell; 406 407 if (logged_in) { 408#if DOANONYMOUS 409 if (guest) { 410 reply(530, "Can't change user from guest login."); 411 return -1; 412 } 413#endif /* DOANONMOUS */ 414 end_login(); 415 } 416 askpasswd = 1; 417#if DOANONYMOUS 418 guest = 0; 419 if (!strcmp(name, "ftp") || !strcmp(name, "anonymous")) 420 if (!checkuser("ftp") && !checkuser("anonymous")) 421 if ((pw = sgetpwnam("ftp")) != NULL) { 422 guest = 1; 423 askpasswd = 1; 424 reply(331, "Guest login ok, send your e-mail address as your password."); 425 syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost); 426 return 0; 427 } 428#endif /* DOANONYMOUS */ 429 if (pw = sgetpwnam(name)) { 430 if ((shell = pw->pw_shell) == NULL || *shell == 0) 431 shell = _PATH_BSHELL; 432 while ((cp = getusershell()) != NULL) 433 if (!strcmp(cp, shell)) 434 break; 435 endusershell(); 436 if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) { 437#if DEBUG 438 if (!cp) 439 syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell); 440 if (checkuser(name)) 441 syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?"); 442 if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) 443 syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd); 444#endif /* DEBUG */ 445 pw = (struct passwd *) NULL; 446 askpasswd = -1; 447 } 448 } 449 { 450 char prompt[OPIE_CHALLENGE_MAX + 1]; 451 452 opiechallenge(&opiestate, name, prompt); 453 454 if (askpasswd == -1) { 455 syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost); 456 pwok = 0; 457 } else 458 pwok = af_pwok && opiealways(pw->pw_dir); 459 460#if NEW_PROMPTS 461 reply(331, "Response to %s %s for %s.", prompt, 462#else /* NEW_PROMPTS */ 463 reply(331, "OTP response %s %s for %s.", prompt, 464#endif /* NEW_PROMPTS */ 465 pwok ? "requested" : "required", name); 466 } 467 /* Delay before reading passwd after first failed attempt to slow down 468 passwd-guessing programs. */ 469 if (login_attempts) 470 sleep((unsigned) login_attempts); 471 472 return 0; 473} 474 475/* 476 * Check if a user is in the file _PATH_FTPUSERS 477 */ 478static int checkuser FUNCTION((name), char *name) 479{ 480 register FILE *fd; 481 register char *p; 482 char line[BUFSIZ]; 483 484 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 485 while (fgets(line, sizeof(line), fd) != NULL) 486 if ((p = strchr(line, '\n')) != NULL) { 487 *p = '\0'; 488 if (line[0] == '#') 489 continue; 490 if (!strcmp(line, name)) { 491 fclose(fd); 492 return (1); 493 } 494 } 495 fclose(fd); 496 } 497 return (0); 498} 499 500/* 501 * Terminate login as previous user, if any, resetting state; 502 * used when USER command is given or login fails. 503 */ 504static VOIDRET end_login FUNCTION_NOARGS 505{ 506 disable_signalling(); 507 if (seteuid((uid_t) 0)) 508 syslog(LOG_ERR, "Can't set euid"); 509 if (logged_in) 510 opielogwtmp(ttyline, "", ""); 511 pw = NULL; 512 logged_in = 0; 513#if DOANONYMOUS 514 guest = 0; 515#endif /* DOANONYMOUS */ 516 enable_signalling(); 517} 518 519VOIDRET pass FUNCTION((passwd), char *passwd) 520{ 521 int legit = askpasswd + 1, i; 522 523 if (logged_in || askpasswd == 0) { 524 reply(503, "Login with USER first."); 525 return; 526 } 527 askpasswd = 0; 528 529#if DOANONYMOUS 530 if (!guest) { /* "ftp" is only account allowed no password */ 531#endif /* DOANONYMOUS */ 532 i = opieverify(&opiestate, passwd); 533 if (legit && i && pwok) 534 i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd); 535 if (!legit || i) { 536 reply(530, "Login incorrect."); 537 pw = NULL; 538 if (login_attempts++ >= 5) { 539 syslog(LOG_WARNING, 540 "Repeated login failures for user %s from %s", 541 pw->pw_name, remotehost); 542 exit(0); 543 } 544 return; 545 } 546#if DOANONYMOUS 547 } else 548 if ((passwd[0] <= ' ') || checkuser(passwd)) { 549 reply(530, "No identity, no service."); 550 syslog(LOG_DEBUG, "Bogus address: %s", passwd); 551 exit(0); 552 } 553#endif /* DOANONYMOUS */ 554 login_attempts = 0; /* this time successful */ 555 if (setegid((gid_t) pw->pw_gid) < 0) { 556 reply(550, "Can't set gid."); 557 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno); 558 return; 559 } 560 initgroups(pw->pw_name, pw->pw_gid); 561 562 /* open wtmp before chroot */ 563 sprintf(ttyline, "ftp%d", getpid()); 564 opielogwtmp(ttyline, pw->pw_name, remotehost); 565 logged_in = 1; 566 567#if DOANONYMOUS 568 if (guest) { 569 /* We MUST do a chdir() after the chroot. Otherwise the old current 570 directory will be accessible as "." outside the new root! */ 571 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 572 reply(550, "Can't set guest privileges."); 573 goto bad; 574 } 575 } else 576#endif /* DOANONYMOUS */ 577 if (chdir(pw->pw_dir) < 0) { 578 if (chdir("/") < 0) { 579 reply(530, "User %s: can't change directory to %s.", 580 pw->pw_name, pw->pw_dir); 581 goto bad; 582 } else 583 lreply(230, "No directory! Logging in with home=/"); 584 } 585/* This patch was contributed by an OPIE user. We don't know what it 586 does, exactly. It may or may not work. */ 587#ifdef _AIX 588 { 589 priv_t priv; 590 priv.pv_priv[0] = 0; 591 priv.pv_priv[1] = 0; 592 setgroups(NULL, NULL); 593 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH, 594 &priv, sizeof(priv_t)) < 0 || 595 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 || 596 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 || 597 seteuid((uid_t)pw->pw_uid) < 0) { 598 reply(550, "Can't set uid (_AIX3)."); 599 goto bad; 600 } 601 } 602#else /* _AIX */ 603 if (seteuid((uid_t) pw->pw_uid) < 0) { 604 reply(550, "Can't set uid."); 605 goto bad; 606 } 607#endif /* _AIX */ 608 /* 609 * Display a login message, if it exists. 610 * N.B. reply(230,) must follow the message. 611 */ 612 { 613 FILE *fd; 614 615 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 616 char *cp, line[128]; 617 618 while (fgets(line, sizeof(line), fd) != NULL) { 619 if ((cp = strchr(line, '\n')) != NULL) 620 *cp = '\0'; 621 lreply(230, "%s", line); 622 } 623 (void) fflush(stdout); 624 (void) fclose(fd); 625 } 626 } 627#if DOANONYMOUS 628 if (guest) { 629 reply(230, "Guest login ok, access restrictions apply."); 630#if DOTITLE 631 sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 632 sizeof(proctitle) - sizeof(remotehost) - 633 sizeof(": anonymous/"), passwd); 634 setproctitle(proctitle); 635#endif /* DOTITLE */ 636 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s", 637 remotehost, passwd); 638 } else 639#endif /* DOANONYMOUS */ 640 { 641 reply(230, "User %s logged in.", pw->pw_name); 642 643#if DOTITLE 644 sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 645 setproctitle(proctitle); 646#endif /* DOTITLE */ 647 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name); 648 } 649 home = pw->pw_dir; /* home dir for globbing */ 650 umask(defumask); 651 return; 652 653bad: 654 /* Forget all about it... */ 655 end_login(); 656} 657 658VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name) 659{ 660 FILE *fin, *dout; 661 struct stat st; 662 int (*closefunc) (); 663 664 if (cmd == 0) { 665 fin = fopen(name, "r"), closefunc = fclose; 666 st.st_size = 0; 667 } else { 668 char line[BUFSIZ]; 669 670 sprintf(line, cmd, name), name = line; 671 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 672 st.st_size = -1; 673#if HAVE_ST_BLKSIZE 674 st.st_blksize = BUFSIZ; 675#endif /* HAVE_ST_BLKSIZE */ 676 } 677 if (fin == NULL) { 678 if (errno != 0) 679 perror_reply(550, name); 680 return; 681 } 682 if (cmd == 0 && 683 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) { 684 reply(550, "%s: not a plain file.", name); 685 goto done; 686 } 687 if (restart_point) { 688 if (type == TYPE_A) { 689 register int i, n, c; 690 691 n = restart_point; 692 i = 0; 693 while (i++ < n) { 694 if ((c = getc(fin)) == EOF) { 695 perror_reply(550, name); 696 goto done; 697 } 698 if (c == '\n') 699 i++; 700 } 701 } else 702 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) { 703 perror_reply(550, name); 704 goto done; 705 } 706 } 707 dout = dataconn(name, st.st_size, "w"); 708 if (dout == NULL) 709 goto done; 710#if HAVE_ST_BLKSIZE 711 send_data(fin, dout, st.st_blksize); 712#else /* HAVE_ST_BLKSIZE */ 713 send_data(fin, dout, BUFSIZ); 714#endif /* HAVE_ST_BLKSIZE */ 715 fclose(dout); 716 data = -1; 717 pdata = -1; 718done: 719 (*closefunc) (fin); 720} 721 722VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique) 723{ 724 FILE *fout, *din; 725 struct stat st; 726 int (*closefunc) (); 727 728 if (unique && stat(name, &st) == 0 && 729 (name = gunique(name)) == NULL) 730 return; 731 732 if (restart_point) 733 mode = "r+w"; 734 fout = fopen(name, mode); 735 closefunc = fclose; 736 if (fout == NULL) { 737 perror_reply(553, name); 738 return; 739 } 740 if (restart_point) { 741 if (type == TYPE_A) { 742 register int i, n, c; 743 744 n = restart_point; 745 i = 0; 746 while (i++ < n) { 747 if ((c = getc(fout)) == EOF) { 748 perror_reply(550, name); 749 goto done; 750 } 751 if (c == '\n') 752 i++; 753 } 754 /* We must do this seek to "current" position because we are changing 755 from reading to writing. */ 756 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) { 757 perror_reply(550, name); 758 goto done; 759 } 760 } else 761 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) { 762 perror_reply(550, name); 763 goto done; 764 } 765 } 766 din = dataconn(name, (off_t) - 1, "r"); 767 if (din == NULL) 768 goto done; 769 if (receive_data(din, fout) == 0) { 770 if (unique) 771 reply(226, "Transfer complete (unique file name:%s).", 772 name); 773 else 774 reply(226, "Transfer complete."); 775 } 776 fclose(din); 777 data = -1; 778 pdata = -1; 779done: 780 (*closefunc) (fout); 781} 782 783static FILE *getdatasock FUNCTION((mode), char *mode) 784{ 785 int s, on = 1, tries; 786 787 if (data >= 0) 788 return (fdopen(data, mode)); 789 disable_signalling(); 790 if (seteuid((uid_t) 0)) 791 syslog(LOG_ERR, "Can't set euid"); 792 s = socket(AF_INET, SOCK_STREAM, 0); 793 if (s < 0) 794 goto bad; 795 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 796 (char *) &on, sizeof(on)) < 0) 797 goto bad; 798 /* anchor socket to avoid multi-homing problems */ 799 data_source.sin_family = AF_INET; 800 data_source.sin_addr = ctrl_addr.sin_addr; 801 for (tries = 1;; tries++) { 802 if (bind(s, (struct sockaddr *) & data_source, 803 sizeof(data_source)) >= 0) 804 break; 805 if (errno != EADDRINUSE || tries > 10) 806 goto bad; 807 sleep(tries); 808 } 809 if (seteuid((uid_t) pw->pw_uid)) 810 syslog(LOG_ERR, "Can't set euid"); 811 enable_signalling(); 812#ifdef IP_TOS 813 on = IPTOS_THROUGHPUT; 814 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0) 815 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 816#endif 817 return (fdopen(s, mode)); 818bad: 819 { 820 int t = errno; 821 822 if (seteuid((uid_t) pw->pw_uid)) 823 syslog(LOG_ERR, "Can't set euid"); 824 enable_signalling(); 825 close(s); 826 827 errno = t; 828 } 829 return (NULL); 830} 831 832static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode) 833{ 834 char sizebuf[32]; 835 FILE *file; 836 int retry = 0; 837#ifdef IP_TOS 838 int tos; 839#endif /* IP_TOS */ 840 841 file_size = size; 842 byte_count = 0; 843 if (size != (off_t) - 1) 844 sprintf(sizebuf, " (%ld bytes)", size); 845 else 846 strcpy(sizebuf, ""); 847 if (pdata >= 0) { 848 struct sockaddr_in from; 849 int s, fromlen = sizeof(from); 850 851 s = accept(pdata, (struct sockaddr *) & from, &fromlen); 852 if (s < 0) { 853 reply(425, "Can't open data connection."); 854 close(pdata); 855 pdata = -1; 856 return (NULL); 857 } 858 close(pdata); 859 pdata = s; 860#ifdef IP_TOS 861 tos = IPTOS_LOWDELAY; 862 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos, 863 sizeof(int)); 864 865#endif 866 reply(150, "Opening %s mode data connection for %s%s.", 867 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 868 return (fdopen(pdata, mode)); 869 } 870 if (data >= 0) { 871 reply(125, "Using existing data connection for %s%s.", 872 name, sizebuf); 873 usedefault = 1; 874 return (fdopen(data, mode)); 875 } 876 if (usedefault) 877 data_dest = his_addr; 878 usedefault = 1; 879 file = getdatasock(mode); 880 if (file == NULL) { 881 reply(425, "Can't create data socket (%s,%d): %s.", 882 inet_ntoa(data_source.sin_addr), 883 ntohs(data_source.sin_port), strerror(errno)); 884 return (NULL); 885 } 886 data = fileno(file); 887 while (connect(data, (struct sockaddr *) & data_dest, 888 sizeof(data_dest)) < 0) { 889 if (errno == EADDRINUSE && retry < swaitmax) { 890 sleep((unsigned) swaitint); 891 retry += swaitint; 892 continue; 893 } 894 perror_reply(425, "Can't build data connection"); 895 fclose(file); 896 data = -1; 897 return (NULL); 898 } 899 reply(150, "Opening %s mode data connection for %s%s.", 900 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 901 return (file); 902} 903 904/* 905 * Tranfer the contents of "instr" to 906 * "outstr" peer using the appropriate 907 * encapsulation of the data subject 908 * to Mode, Structure, and Type. 909 * 910 * NB: Form isn't handled. 911 */ 912static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize) 913{ 914 register int c, cnt; 915 register char *buf; 916 int netfd, filefd; 917 918 transflag++; 919 if (setjmp(urgcatch)) { 920 transflag = 0; 921 return; 922 } 923 switch (type) { 924 925 case TYPE_A: 926 while ((c = getc(instr)) != EOF) { 927 byte_count++; 928 if (c == '\n') { 929 if (ferror(outstr)) 930 goto data_err; 931 putc('\r', outstr); 932 } 933 putc(c, outstr); 934 } 935 fflush(outstr); 936 transflag = 0; 937 if (ferror(instr)) 938 goto file_err; 939 if (ferror(outstr)) 940 goto data_err; 941 reply(226, "Transfer complete."); 942 return; 943 944 case TYPE_I: 945 case TYPE_L: 946 if ((buf = malloc((u_int) blksize)) == NULL) { 947 transflag = 0; 948 perror_reply(451, "Local resource failure: malloc"); 949 return; 950 } 951 netfd = fileno(outstr); 952 filefd = fileno(instr); 953 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 && 954 write(netfd, buf, cnt) == cnt) 955 byte_count += cnt; 956 transflag = 0; 957 free(buf); 958 if (cnt != 0) { 959 if (cnt < 0) 960 goto file_err; 961 goto data_err; 962 } 963 reply(226, "Transfer complete."); 964 return; 965 default: 966 transflag = 0; 967 reply(550, "Unimplemented TYPE %d in send_data", type); 968 return; 969 } 970 971data_err: 972 transflag = 0; 973 perror_reply(426, "Data connection"); 974 return; 975 976file_err: 977 transflag = 0; 978 perror_reply(551, "Error on input file"); 979} 980 981/* 982 * Transfer data from peer to 983 * "outstr" using the appropriate 984 * encapulation of the data subject 985 * to Mode, Structure, and Type. 986 * 987 * N.B.: Form isn't handled. 988 */ 989static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr) 990{ 991 register int c; 992 int cnt, bare_lfs = 0; 993 char buf[BUFSIZ]; 994 995 transflag++; 996 if (setjmp(urgcatch)) { 997 transflag = 0; 998 return (-1); 999 } 1000 switch (type) { 1001 1002 case TYPE_I: 1003 case TYPE_L: 1004 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 1005 if (write(fileno(outstr), buf, cnt) != cnt) 1006 goto file_err; 1007 byte_count += cnt; 1008 } 1009 if (cnt < 0) 1010 goto data_err; 1011 transflag = 0; 1012 return (0); 1013 1014 case TYPE_E: 1015 reply(553, "TYPE E not implemented."); 1016 transflag = 0; 1017 return (-1); 1018 1019 case TYPE_A: 1020 while ((c = getc(instr)) != EOF) { 1021 byte_count++; 1022 if (c == '\n') 1023 bare_lfs++; 1024 while (c == '\r') { 1025 if (ferror(outstr)) 1026 goto data_err; 1027 if ((c = getc(instr)) != '\n') { 1028 putc('\r', outstr); 1029 if (c == '\0' || c == EOF) 1030 goto contin2; 1031 } 1032 } 1033 putc(c, outstr); 1034 contin2:; 1035 } 1036 fflush(outstr); 1037 if (ferror(instr)) 1038 goto data_err; 1039 if (ferror(outstr)) 1040 goto file_err; 1041 transflag = 0; 1042 if (bare_lfs) { 1043 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 1044 printf(" File may not have transferred correctly.\r\n"); 1045 } 1046 return (0); 1047 default: 1048 reply(550, "Unimplemented TYPE %d in receive_data", type); 1049 transflag = 0; 1050 return (-1); 1051 } 1052 1053data_err: 1054 transflag = 0; 1055 perror_reply(426, "Data Connection"); 1056 return (-1); 1057 1058file_err: 1059 transflag = 0; 1060 perror_reply(452, "Error writing file"); 1061 return (-1); 1062} 1063 1064VOIDRET statfilecmd FUNCTION((filename), char *filename) 1065{ 1066 char line[BUFSIZ]; 1067 FILE *fin; 1068 int c; 1069 1070#if HAVE_LS_G_FLAG 1071 sprintf(line, "%s %s", "/bin/ls -lgA", filename); 1072#else /* HAVE_LS_G_FLAG */ 1073 sprintf(line, "%s %s", "/bin/ls -lA", filename); 1074#endif /* HAVE_LS_G_FLAG */ 1075 fin = ftpd_popen(line, "r"); 1076 lreply(211, "status of %s:", filename); 1077 while ((c = getc(fin)) != EOF) { 1078 if (c == '\n') { 1079 if (ferror(stdout)) { 1080 perror_reply(421, "control connection"); 1081 ftpd_pclose(fin); 1082 dologout(1); 1083 /* NOTREACHED */ 1084 } 1085 if (ferror(fin)) { 1086 perror_reply(551, filename); 1087 ftpd_pclose(fin); 1088 return; 1089 } 1090 putc('\r', stdout); 1091 } 1092 putc(c, stdout); 1093 } 1094 ftpd_pclose(fin); 1095 reply(211, "End of Status"); 1096} 1097 1098VOIDRET statcmd FUNCTION_NOARGS 1099{ 1100/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */ 1101 struct sockaddr_in *sin; 1102 u_char *a, *p; 1103 1104 lreply(211, "FTP server status:"); 1105 printf(" \r\n"); 1106 printf(" Connected to %s", remotehost); 1107 if (!isdigit(remotehost[0])) 1108 printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 1109 printf("\r\n"); 1110 if (logged_in) { 1111#if DOANONYMOUS 1112 if (guest) 1113 printf(" Logged in anonymously\r\n"); 1114 else 1115#endif /* DOANONYMOUS */ 1116 printf(" Logged in as %s\r\n", pw->pw_name); 1117 } else 1118 if (askpasswd) 1119 printf(" Waiting for password\r\n"); 1120 else 1121 printf(" Waiting for user name\r\n"); 1122 if (data != -1) 1123 printf(" Data connection open\r\n"); 1124 else 1125 if (pdata != -1) { 1126 printf(" in Passive mode"); 1127 sin = &pasv_addr; 1128 goto printaddr; 1129 } else 1130 if (usedefault == 0) { 1131 printf(" PORT"); 1132 sin = &data_dest; 1133 printaddr: 1134 a = (u_char *) & sin->sin_addr; 1135 p = (u_char *) & sin->sin_port; 1136#define UC(b) (((int) b) & 0xff) 1137 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 1138 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1139#undef UC 1140 } else 1141 printf(" No data connection\r\n"); 1142 reply(211, "End of status"); 1143} 1144 1145VOIDRET opiefatal FUNCTION((s), char *s) 1146{ 1147 reply(451, "Error in server: %s\n", s); 1148 reply(221, "Closing connection due to server error."); 1149 dologout(0); 1150 /* NOTREACHED */ 1151} 1152 1153static VOIDRET ack FUNCTION((s), char *s) 1154{ 1155 reply(250, "%s command successful.", s); 1156} 1157 1158VOIDRET nack FUNCTION((s), char *s) 1159{ 1160 reply(502, "%s command not implemented.", s); 1161} 1162 1163VOIDRET yyerror FUNCTION((s), char *s) 1164{ 1165 char *cp; 1166 1167 if (cp = strchr(cbuf, '\n')) 1168 *cp = '\0'; 1169 reply(500, "'%s': command not understood.", cbuf); 1170} 1171 1172VOIDRET delete FUNCTION((name), char *name) 1173{ 1174 struct stat st; 1175 1176 if (stat(name, &st) < 0) { 1177 perror_reply(550, name); 1178 return; 1179 } 1180 if ((st.st_mode & S_IFMT) == S_IFDIR) { 1181 if (rmdir(name) < 0) { 1182 perror_reply(550, name); 1183 return; 1184 } 1185 goto done; 1186 } 1187 if (unlink(name) < 0) { 1188 perror_reply(550, name); 1189 return; 1190 } 1191done: 1192 ack("DELE"); 1193} 1194 1195VOIDRET cwd FUNCTION((path), char *path) 1196{ 1197 if (chdir(path) < 0) 1198 perror_reply(550, path); 1199 else 1200 ack("CWD"); 1201} 1202 1203VOIDRET makedir FUNCTION((name), char *name) 1204{ 1205 if (mkdir(name, 0777) < 0) 1206 perror_reply(550, name); 1207 else 1208 reply(257, "MKD command successful."); 1209} 1210 1211VOIDRET removedir FUNCTION((name), char *name) 1212{ 1213 if (rmdir(name) < 0) 1214 perror_reply(550, name); 1215 else 1216 ack("RMD"); 1217} 1218 1219VOIDRET pwd FUNCTION_NOARGS 1220{ 1221 char path[MAXPATHLEN + 1]; 1222 1223 if (getcwd(path, MAXPATHLEN) == (char *) NULL) 1224 reply(550, "%s.", path); 1225 else 1226 reply(257, "\"%s\" is current directory.", path); 1227} 1228 1229char *renamefrom FUNCTION((name), char *name) 1230{ 1231 struct stat st; 1232 1233 if (stat(name, &st) < 0) { 1234 perror_reply(550, name); 1235 return ((char *) 0); 1236 } 1237 reply(350, "File exists, ready for destination name"); 1238 return (name); 1239} 1240 1241VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to) 1242{ 1243 if (rename(from, to) < 0) 1244 perror_reply(550, "rename"); 1245 else 1246 ack("RNTO"); 1247} 1248 1249static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin) 1250{ 1251 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr, 1252 sizeof(struct in_addr), AF_INET); 1253 time_t t, time(); 1254 1255 if (hp) 1256 strncpy(remotehost, hp->h_name, sizeof(remotehost)); 1257 else 1258 strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost)); 1259#if DOTITLE 1260 sprintf(proctitle, "%s: connected", remotehost); 1261 setproctitle(proctitle); 1262#endif /* DOTITLE */ 1263 1264 t = time((time_t *) 0); 1265 syslog(LOG_INFO, "connection from %s at %s", 1266 remotehost, ctime(&t)); 1267} 1268 1269/* 1270 * Record logout in wtmp file 1271 * and exit with supplied status. 1272 */ 1273VOIDRET dologout FUNCTION((status), int status) 1274{ 1275 disable_signalling(); 1276 if (logged_in) { 1277 if (seteuid((uid_t) 0)) 1278 syslog(LOG_ERR, "Can't set euid"); 1279 opielogwtmp(ttyline, "", ""); 1280 } 1281 /* beware of flushing buffers after a SIGPIPE */ 1282 _exit(status); 1283} 1284 1285static VOIDRET myoob FUNCTION((input), int input) 1286{ 1287 char *cp; 1288 1289 /* only process if transfer occurring */ 1290 if (!transflag) 1291 return; 1292 cp = tmpline; 1293 if (getline(cp, 7, stdin) == NULL) { 1294 reply(221, "You could at least say goodbye."); 1295 dologout(0); 1296 } 1297 upper(cp); 1298 if (strcmp(cp, "ABOR\r\n") == 0) { 1299 tmpline[0] = '\0'; 1300 reply(426, "Transfer aborted. Data connection closed."); 1301 reply(226, "Abort successful"); 1302 longjmp(urgcatch, 1); 1303 } 1304 if (strcmp(cp, "STAT\r\n") == 0) { 1305 if (file_size != (off_t) - 1) 1306 reply(213, "Status: %lu of %lu bytes transferred", 1307 byte_count, file_size); 1308 else 1309 reply(213, "Status: %lu bytes transferred", byte_count); 1310 } 1311} 1312 1313/* 1314 * Note: a response of 425 is not mentioned as a possible response to 1315 * the PASV command in RFC959. However, it has been blessed as 1316 * a legitimate response by Jon Postel in a telephone conversation 1317 * with Rick Adams on 25 Jan 89. 1318 */ 1319VOIDRET passive FUNCTION_NOARGS 1320{ 1321 int len; 1322 register char *p, *a; 1323 1324 pdata = socket(AF_INET, SOCK_STREAM, 0); 1325 if (pdata < 0) { 1326 perror_reply(425, "Can't open passive connection"); 1327 return; 1328 } 1329 pasv_addr = ctrl_addr; 1330 pasv_addr.sin_port = 0; 1331 if (seteuid((uid_t) 0)) 1332 syslog(LOG_ERR, "Can't set euid"); 1333 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) { 1334 seteuid((uid_t) pw->pw_uid); 1335 goto pasv_error; 1336 } 1337 if (seteuid((uid_t) pw->pw_uid)) 1338 syslog(LOG_ERR, "Can't set euid"); 1339 len = sizeof(pasv_addr); 1340 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0) 1341 goto pasv_error; 1342 if (listen(pdata, 1) < 0) 1343 goto pasv_error; 1344 a = (char *) &pasv_addr.sin_addr; 1345 p = (char *) &pasv_addr.sin_port; 1346 1347#define UC(b) (((int) b) & 0xff) 1348 1349 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 1350 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1351 return; 1352 1353pasv_error: 1354 close(pdata); 1355 pdata = -1; 1356 perror_reply(425, "Can't open passive connection"); 1357 return; 1358} 1359 1360/* 1361 * Generate unique name for file with basename "local". 1362 * The file named "local" is already known to exist. 1363 * Generates failure reply on error. 1364 */ 1365static char *gunique FUNCTION((local), char *local) 1366{ 1367 static char new[MAXPATHLEN+1]; 1368 struct stat st; 1369 char *cp = strrchr(local, '/'); 1370 int count = 0; 1371 1372 if (cp) 1373 *cp = '\0'; 1374 if (stat(cp ? local : ".", &st) < 0) { 1375 perror_reply(553, cp ? local : "."); 1376 return ((char *) 0); 1377 } 1378 if (cp) 1379 *cp = '/'; 1380 strcpy(new, local); 1381 cp = new + strlen(new); 1382 *cp++ = '.'; 1383 for (count = 1; count < 100; count++) { 1384 sprintf(cp, "%d", count); 1385 if (stat(new, &st) < 0) 1386 return (new); 1387 } 1388 reply(452, "Unique file name cannot be created."); 1389 return ((char *) 0); 1390} 1391 1392/* 1393 * Format and send reply containing system error number. 1394 */ 1395VOIDRET perror_reply FUNCTION((code, string), int code AND char *string) 1396{ 1397 reply(code, "%s: %s.", string, strerror(errno)); 1398} 1399 1400static char *onefile[] = 1401{ 1402 "", 1403 0 1404}; 1405 1406VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles) 1407{ 1408 struct stat st; 1409 DIR *dirp = NULL; 1410 struct dirent *dir; 1411 FILE *dout = NULL; 1412 register char **dirlist, *dirname; 1413 int simple = 0; 1414 1415 if (strpbrk(whichfiles, "~{[*?") != NULL) { 1416 extern char **ftpglob(), *globerr; 1417 1418 globerr = NULL; 1419 dirlist = ftpglob(whichfiles); 1420 if (globerr != NULL) { 1421 reply(550, globerr); 1422 return; 1423 } else 1424 if (dirlist == NULL) { 1425 errno = ENOENT; 1426 perror_reply(550, whichfiles); 1427 return; 1428 } 1429 } else { 1430 onefile[0] = whichfiles; 1431 dirlist = onefile; 1432 simple = 1; 1433 } 1434 1435 if (setjmp(urgcatch)) { 1436 transflag = 0; 1437 return; 1438 } 1439 while (dirname = *dirlist++) { 1440 if (stat(dirname, &st) < 0) { 1441 /* If user typed "ls -l", etc, and the client used NLST, do what the 1442 user meant. */ 1443 if (dirname[0] == '-' && *dirlist == NULL && 1444 transflag == 0) { 1445 retrieve("/bin/ls %s", dirname); 1446 return; 1447 } 1448 perror_reply(550, whichfiles); 1449 if (dout != NULL) { 1450 fclose(dout); 1451 transflag = 0; 1452 data = -1; 1453 pdata = -1; 1454 } 1455 return; 1456 } 1457 if ((st.st_mode & S_IFMT) == S_IFREG) { 1458 if (dout == NULL) { 1459 dout = dataconn("file list", (off_t) - 1, "w"); 1460 if (dout == NULL) 1461 return; 1462 transflag++; 1463 } 1464 fprintf(dout, "%s%s\n", dirname, 1465 type == TYPE_A ? "\r" : ""); 1466 byte_count += strlen(dirname) + 1; 1467 continue; 1468 } else 1469 if ((st.st_mode & S_IFMT) != S_IFDIR) 1470 continue; 1471 1472 if ((dirp = opendir(dirname)) == NULL) 1473 continue; 1474 1475 while ((dir = readdir(dirp)) != NULL) { 1476 char nbuf[MAXPATHLEN+1]; 1477 1478 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1)) 1479 continue; 1480 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 1481 (strlen(dir->d_name) == 2)) 1482 continue; 1483 1484 sprintf(nbuf, "%s/%s", dirname, dir->d_name); 1485 1486 /* We have to do a stat to insure it's not a directory or special file. */ 1487 if (simple || (stat(nbuf, &st) == 0 && 1488 (st.st_mode & S_IFMT) == S_IFREG)) { 1489 if (dout == NULL) { 1490 dout = dataconn("file list", (off_t) - 1, "w"); 1491 if (dout == NULL) 1492 return; 1493 transflag++; 1494 } 1495 if (nbuf[0] == '.' && nbuf[1] == '/') 1496 fprintf(dout, "%s%s\n", &nbuf[2], 1497 type == TYPE_A ? "\r" : ""); 1498 else 1499 fprintf(dout, "%s%s\n", nbuf, 1500 type == TYPE_A ? "\r" : ""); 1501 byte_count += strlen(nbuf) + 1; 1502 } 1503 } 1504 closedir(dirp); 1505 } 1506 1507 if (dout == NULL) 1508 reply(550, "No files found."); 1509 else 1510 if (ferror(dout) != 0) 1511 perror_reply(550, "Data connection"); 1512 else 1513 reply(226, "Transfer complete."); 1514 1515 transflag = 0; 1516 if (dout != NULL) 1517 fclose(dout); 1518 data = -1; 1519 pdata = -1; 1520} 1521 1522#if DOTITLE 1523/* 1524 * clobber argv so ps will show what we're doing. 1525 * (stolen from sendmail) 1526 * warning, since this is usually started from inetd.conf, it 1527 * often doesn't have much of an environment or arglist to overwrite. 1528 */ 1529VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c) 1530{ 1531 register char *p, *bp, ch; 1532 register int i; 1533 char buf[BUFSIZ]; 1534 1535 sprintf(buf, fmt, a, b, c); 1536 1537 /* make ps print our process name */ 1538 p = Argv[0]; 1539 *p++ = '-'; 1540 1541 i = strlen(buf); 1542 if (i > LastArgv - p - 2) { 1543 i = LastArgv - p - 2; 1544 buf[i] = '\0'; 1545 } 1546 bp = buf; 1547 while (ch = *bp++) 1548 if (ch != '\n' && ch != '\r') 1549 *p++ = ch; 1550 while (p < LastArgv) 1551 *p++ = ' '; 1552} 1553#endif /* DOTITLE */ 1554 1555VOIDRET catchexit FUNCTION_NOARGS 1556{ 1557 closelog(); 1558} 1559 1560int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[]) 1561{ 1562 int addrlen, on = 1; 1563 char *cp; 1564#ifdef IP_TOS 1565 int tos; 1566#endif /* IP_TOS */ 1567 1568 { 1569 int i; 1570 1571 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) 1572 close(i); 1573 } 1574 1575 /* LOG_NDELAY sets up the logging connection immediately, necessary for 1576 anonymous ftp's that chroot and can't do it later. */ 1577 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 1578 atexit(catchexit); 1579 addrlen = sizeof(his_addr); 1580 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) { 1581 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); 1582 exit(1); 1583 } 1584 addrlen = sizeof(ctrl_addr); 1585 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) { 1586 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]); 1587 exit(1); 1588 } 1589#ifdef IP_TOS 1590 tos = IPTOS_LOWDELAY; 1591 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0) 1592 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 1593#endif 1594 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 1595 debug = 0; 1596#if DOTITLE 1597 /* Save start and extent of argv for setproctitle. */ 1598 Argv = argv; 1599 while (*envp) 1600 envp++; 1601 LastArgv = envp[-1] + strlen(envp[-1]); 1602#endif /* DOTITLE */ 1603 1604 argc--, argv++; 1605 while (argc > 0 && *argv[0] == '-') { 1606 for (cp = &argv[0][1]; *cp; cp++) 1607 switch (*cp) { 1608 1609 case 'v': 1610 debug = 1; 1611 break; 1612 1613 case 'd': 1614 debug = 1; 1615 break; 1616 1617 case 'l': 1618 break; 1619 1620 case 't': 1621 timeout = atoi(++cp); 1622 if (maxtimeout < timeout) 1623 maxtimeout = timeout; 1624 goto nextopt; 1625 1626 case 'T': 1627 maxtimeout = atoi(++cp); 1628 if (timeout > maxtimeout) 1629 timeout = maxtimeout; 1630 goto nextopt; 1631 1632 case 'u': 1633 { 1634 int val = 0; 1635 1636 while (*++cp && *cp >= '0' && *cp <= '9') 1637 val = val * 8 + *cp - '0'; 1638 if (*cp) 1639 fprintf(stderr, "ftpd: Bad value for -u\n"); 1640 else 1641 defumask = val; 1642 goto nextopt; 1643 } 1644 1645 default: 1646 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 1647 *cp); 1648 break; 1649 } 1650nextopt: 1651 argc--, argv++; 1652 } 1653 freopen(_PATH_DEVNULL, "w", stderr); 1654 signal(SIGCHLD, SIG_IGN); 1655 enable_signalling(); 1656 1657 /* Try to handle urgent data inline */ 1658#ifdef SO_OOBINLINE 1659 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0) 1660 syslog(LOG_ERR, "setsockopt: %m"); 1661#endif 1662 1663#ifdef F_SETOWN 1664 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 1665 syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 1666#endif 1667 dolog(&his_addr); 1668 /* Set up default state */ 1669 data = -1; 1670 type = TYPE_A; 1671 form = FORM_N; 1672 stru = STRU_F; 1673 mode = MODE_S; 1674 tmpline[0] = '\0'; 1675 af_pwok = opieaccessfile(remotehost); 1676 1677 { 1678 FILE *fd; 1679 char line[128]; 1680 1681 /* If logins are disabled, print out the message. */ 1682 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 1683 while (fgets(line, sizeof(line), fd) != NULL) { 1684 if ((cp = strchr(line, '\n')) != NULL) 1685 *cp = '\0'; 1686 lreply(530, "%s", line); 1687 } 1688 (void) fflush(stdout); 1689 (void) fclose(fd); 1690 reply(530, "System not available."); 1691 exit(0); 1692 } 1693 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 1694 while (fgets(line, sizeof(line), fd) != NULL) { 1695 if ((cp = strchr(line, '\n')) != NULL) 1696 *cp = '\0'; 1697 lreply(220, "%s", line); 1698 } 1699 (void) fflush(stdout); 1700 (void) fclose(fd); 1701 /* reply(220,) must follow */ 1702 } 1703 }; 1704 1705 reply(220, "FTP server ready."); 1706 1707 setjmp(errcatch); 1708 for (;;) 1709 yyparse(); 1710 /* NOTREACHED */ 1711 return 0; 1712} 1713