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