ftpd.c revision 33782
1/* 2 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#if 0 35#ifndef lint 36static char copyright[] = 37"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39#endif /* not lint */ 40#endif 41 42#ifndef lint 43#if 0 44static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; 45#endif 46static const char rcsid[] = 47 "$Id: ftpd.c,v 1.44 1997/12/24 19:13:22 imp Exp $"; 48#endif /* not lint */ 49 50/* 51 * FTP server. 52 */ 53#include <sys/param.h> 54#include <sys/stat.h> 55#include <sys/ioctl.h> 56#include <sys/socket.h> 57#include <sys/wait.h> 58#include <sys/mman.h> 59 60#include <netinet/in.h> 61#include <netinet/in_systm.h> 62#include <netinet/ip.h> 63#include <netinet/tcp.h> 64 65#define FTP_NAMES 66#include <arpa/ftp.h> 67#include <arpa/inet.h> 68#include <arpa/telnet.h> 69 70#include <ctype.h> 71#include <dirent.h> 72#include <err.h> 73#include <errno.h> 74#include <fcntl.h> 75#include <glob.h> 76#include <limits.h> 77#include <netdb.h> 78#include <pwd.h> 79#include <grp.h> 80#include <setjmp.h> 81#include <signal.h> 82#include <stdio.h> 83#include <stdlib.h> 84#include <string.h> 85#include <syslog.h> 86#include <time.h> 87#include <unistd.h> 88#include <libutil.h> 89#ifdef LOGIN_CAP 90#include <login_cap.h> 91#endif 92 93#ifdef SKEY 94#include <skey.h> 95#endif 96 97#include "pathnames.h" 98#include "extern.h" 99 100#if __STDC__ 101#include <stdarg.h> 102#else 103#include <varargs.h> 104#endif 105 106#ifdef INTERNAL_LS 107static char version[] = "Version 6.00LS"; 108#undef main 109#else 110static char version[] = "Version 6.00"; 111#endif 112 113extern off_t restart_point; 114extern char cbuf[]; 115 116struct sockaddr_in server_addr; 117struct sockaddr_in ctrl_addr; 118struct sockaddr_in data_source; 119struct sockaddr_in data_dest; 120struct sockaddr_in his_addr; 121struct sockaddr_in pasv_addr; 122 123int daemon_mode; 124int data; 125jmp_buf errcatch, urgcatch; 126int logged_in; 127struct passwd *pw; 128int debug; 129int timeout = 900; /* timeout after 15 minutes of inactivity */ 130int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 131int logging; 132int restricted_data_ports = 1; 133int paranoid = 1; /* be extra careful about security */ 134int anon_only = 0; /* Only anonymous ftp allowed */ 135int guest; 136int dochroot; 137int stats; 138int statfd = -1; 139int type; 140int form; 141int stru; /* avoid C keyword */ 142int mode; 143int usedefault = 1; /* for data transfers */ 144int pdata = -1; /* for passive mode */ 145sig_atomic_t transflag; 146off_t file_size; 147off_t byte_count; 148#if !defined(CMASK) || CMASK == 0 149#undef CMASK 150#define CMASK 027 151#endif 152int defumask = CMASK; /* default umask value */ 153char tmpline[7]; 154char *hostname; 155#ifdef VIRTUAL_HOSTING 156char *ftpuser; 157 158static struct ftphost { 159 struct ftphost *next; 160 struct in_addr hostaddr; 161 char *hostname; 162 char *anonuser; 163 char *statfile; 164 char *welcome; 165 char *loginmsg; 166} *thishost, *firsthost; 167 168#endif 169char remotehost[MAXHOSTNAMELEN]; 170char *ident = NULL; 171 172static char ttyline[20]; 173char *tty = ttyline; /* for klogin */ 174 175#ifdef KERBEROS 176int klogin __P((struct passwd *, char *, char *, char *)); 177#endif 178 179struct in_addr bind_address; 180char *pid_file = NULL; 181 182#if defined(KERBEROS) 183int notickets = 1; 184int noticketsdontcomplain = 1; 185char *krbtkfile_env = NULL; 186#endif 187 188/* 189 * Timeout intervals for retrying connections 190 * to hosts that don't accept PORT cmds. This 191 * is a kludge, but given the problems with TCP... 192 */ 193#define SWAITMAX 90 /* wait at most 90 seconds */ 194#define SWAITINT 5 /* interval between retries */ 195 196int swaitmax = SWAITMAX; 197int swaitint = SWAITINT; 198 199#ifdef SETPROCTITLE 200#ifdef OLD_SETPROCTITLE 201char **Argv = NULL; /* pointer to argument vector */ 202char *LastArgv = NULL; /* end of argv */ 203#endif /* OLD_SETPROCTITLE */ 204char proctitle[LINE_MAX]; /* initial part of title */ 205#endif /* SETPROCTITLE */ 206 207#ifdef SKEY 208int pwok = 0; 209char addr_string[20]; /* XXX */ 210#endif 211 212#define LOGCMD(cmd, file) \ 213 if (logging > 1) \ 214 syslog(LOG_INFO,"%s %s%s", cmd, \ 215 *(file) == '/' ? "" : curdir(), file); 216#define LOGCMD2(cmd, file1, file2) \ 217 if (logging > 1) \ 218 syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 219 *(file1) == '/' ? "" : curdir(), file1, \ 220 *(file2) == '/' ? "" : curdir(), file2); 221#define LOGBYTES(cmd, file, cnt) \ 222 if (logging > 1) { \ 223 if (cnt == (off_t)-1) \ 224 syslog(LOG_INFO,"%s %s%s", cmd, \ 225 *(file) == '/' ? "" : curdir(), file); \ 226 else \ 227 syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 228 cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 229 } 230 231#ifdef VIRTUAL_HOSTING 232static void inithosts __P((void)); 233static void selecthost __P((struct in_addr *)); 234#endif 235static void ack __P((char *)); 236static void myoob __P((int)); 237static int checkuser __P((char *, char *)); 238static FILE *dataconn __P((char *, off_t, char *)); 239static void dolog __P((struct sockaddr_in *)); 240static char *curdir __P((void)); 241static void end_login __P((void)); 242static FILE *getdatasock __P((char *)); 243static char *gunique __P((char *)); 244static void lostconn __P((int)); 245static int receive_data __P((FILE *, FILE *)); 246static void send_data __P((FILE *, FILE *, off_t, off_t, int)); 247static struct passwd * 248 sgetpwnam __P((char *)); 249static char *sgetsave __P((char *)); 250static void reapchild __P((int)); 251static void logxfer __P((char *, long, long)); 252 253static char * 254curdir() 255{ 256 static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 257 258 if (getcwd(path, sizeof(path)-2) == NULL) 259 return (""); 260 if (path[1] != '\0') /* special case for root dir. */ 261 strcat(path, "/"); 262 /* For guest account, skip / since it's chrooted */ 263 return (guest ? path+1 : path); 264} 265 266int 267main(argc, argv, envp) 268 int argc; 269 char *argv[]; 270 char **envp; 271{ 272 int addrlen, ch, on = 1, tos; 273 char *cp, line[LINE_MAX]; 274 FILE *fd; 275 276 tzset(); /* in case no timezone database in ~ftp */ 277 278#ifdef OLD_SETPROCTITLE 279 /* 280 * Save start and extent of argv for setproctitle. 281 */ 282 Argv = argv; 283 while (*envp) 284 envp++; 285 LastArgv = envp[-1] + strlen(envp[-1]); 286#endif /* OLD_SETPROCTITLE */ 287 288 289 bind_address.s_addr = htonl(INADDR_ANY); 290 while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) { 291 switch (ch) { 292 case 'D': 293 daemon_mode++; 294 break; 295 296 case 'd': 297 debug++; 298 break; 299 300 case 'l': 301 logging++; /* > 1 == extra logging */ 302 break; 303 304 case 'R': 305 paranoid = 0; 306 break; 307 308 case 'S': 309 stats++; 310 break; 311 312 case 'T': 313 maxtimeout = atoi(optarg); 314 if (timeout > maxtimeout) 315 timeout = maxtimeout; 316 break; 317 318 case 't': 319 timeout = atoi(optarg); 320 if (maxtimeout < timeout) 321 maxtimeout = timeout; 322 break; 323 324 case 'U': 325 restricted_data_ports = 0; 326 break; 327 328 case 'a': 329 if (!inet_aton(optarg, &bind_address)) 330 errx(1, "invalid address for -a"); 331 break; 332 333 case 'p': 334 pid_file = optarg; 335 break; 336 337 case 'u': 338 { 339 long val = 0; 340 341 val = strtol(optarg, &optarg, 8); 342 if (*optarg != '\0' || val < 0) 343 warnx("bad value for -u"); 344 else 345 defumask = val; 346 break; 347 } 348 case 'A': 349 anon_only = 1; 350 break; 351 352 case 'v': 353 debug = 1; 354 break; 355 356 default: 357 warnx("unknown flag -%c ignored", optopt); 358 break; 359 } 360 } 361 362#ifdef VIRTUAL_HOSTING 363 inithosts(); 364#endif 365 (void) freopen(_PATH_DEVNULL, "w", stderr); 366 367 /* 368 * LOG_NDELAY sets up the logging connection immediately, 369 * necessary for anonymous ftp's that chroot and can't do it later. 370 */ 371 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 372 373 if (daemon_mode) { 374 int ctl_sock, fd; 375 struct servent *sv; 376 377 /* 378 * Detach from parent. 379 */ 380 if (daemon(1, 1) < 0) { 381 syslog(LOG_ERR, "failed to become a daemon"); 382 exit(1); 383 } 384 (void) signal(SIGCHLD, reapchild); 385 /* 386 * Get port number for ftp/tcp. 387 */ 388 sv = getservbyname("ftp", "tcp"); 389 if (sv == NULL) { 390 syslog(LOG_ERR, "getservbyname for ftp failed"); 391 exit(1); 392 } 393 /* 394 * Open a socket, bind it to the FTP port, and start 395 * listening. 396 */ 397 ctl_sock = socket(AF_INET, SOCK_STREAM, 0); 398 if (ctl_sock < 0) { 399 syslog(LOG_ERR, "control socket: %m"); 400 exit(1); 401 } 402 if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, 403 (char *)&on, sizeof(on)) < 0) 404 syslog(LOG_ERR, "control setsockopt: %m");; 405 server_addr.sin_family = AF_INET; 406 server_addr.sin_addr = bind_address; 407 server_addr.sin_port = sv->s_port; 408 if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { 409 syslog(LOG_ERR, "control bind: %m"); 410 exit(1); 411 } 412 if (listen(ctl_sock, 32) < 0) { 413 syslog(LOG_ERR, "control listen: %m"); 414 exit(1); 415 } 416 /* 417 * Atomically write process ID 418 */ 419 if (pid_file) 420 { 421 int fd; 422 char buf[20]; 423 424 fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC 425 | O_NONBLOCK | O_EXLOCK, 0644); 426 if (fd < 0) 427 if (errno == EAGAIN) 428 errx(1, "%s: file locked", pid_file); 429 else 430 err(1, "%s", pid_file); 431 snprintf(buf, sizeof(buf), 432 "%lu\n", (unsigned long) getpid()); 433 if (write(fd, buf, strlen(buf)) < 0) 434 err(1, "%s: write", pid_file); 435 /* Leave the pid file open and locked */ 436 } 437 /* 438 * Loop forever accepting connection requests and forking off 439 * children to handle them. 440 */ 441 while (1) { 442 addrlen = sizeof(his_addr); 443 fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); 444 if (fork() == 0) { 445 /* child */ 446 (void) dup2(fd, 0); 447 (void) dup2(fd, 1); 448 close(ctl_sock); 449 break; 450 } 451 close(fd); 452 } 453 } else { 454 addrlen = sizeof(his_addr); 455 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 456 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 457 exit(1); 458 } 459 } 460 461 (void) signal(SIGCHLD, SIG_IGN); 462 (void) signal(SIGPIPE, lostconn); 463 if ((int)signal(SIGURG, myoob) < 0) 464 syslog(LOG_ERR, "signal: %m"); 465 466#ifdef SKEY 467 strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string)); 468#endif 469 addrlen = sizeof(ctrl_addr); 470 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 471 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 472 exit(1); 473 } 474#ifdef VIRTUAL_HOSTING 475 /* select our identity from virtual host table */ 476 selecthost(&ctrl_addr.sin_addr); 477#endif 478#ifdef IP_TOS 479 tos = IPTOS_LOWDELAY; 480 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 481 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 482#endif 483 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 484 485 /* set this here so klogin can use it... */ 486 (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); 487 488 /* Try to handle urgent data inline */ 489#ifdef SO_OOBINLINE 490 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 491 syslog(LOG_ERR, "setsockopt: %m"); 492#endif 493 494#ifdef F_SETOWN 495 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 496 syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 497#endif 498 dolog(&his_addr); 499 /* 500 * Set up default state 501 */ 502 data = -1; 503 type = TYPE_A; 504 form = FORM_N; 505 stru = STRU_F; 506 mode = MODE_S; 507 tmpline[0] = '\0'; 508 509 /* If logins are disabled, print out the message. */ 510 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 511 while (fgets(line, sizeof(line), fd) != NULL) { 512 if ((cp = strchr(line, '\n')) != NULL) 513 *cp = '\0'; 514 lreply(530, "%s", line); 515 } 516 (void) fflush(stdout); 517 (void) fclose(fd); 518 reply(530, "System not available."); 519 exit(0); 520 } 521#ifdef VIRTUAL_HOSTING 522 if ((fd = fopen(thishost->welcome, "r")) != NULL) { 523#else 524 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 525#endif 526 while (fgets(line, sizeof(line), fd) != NULL) { 527 if ((cp = strchr(line, '\n')) != NULL) 528 *cp = '\0'; 529 lreply(220, "%s", line); 530 } 531 (void) fflush(stdout); 532 (void) fclose(fd); 533 /* reply(220,) must follow */ 534 } 535#ifndef VIRTUAL_HOSTING 536 if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 537 fatal("Ran out of memory."); 538 (void) gethostname(hostname, MAXHOSTNAMELEN); 539#endif 540 reply(220, "%s FTP server (%s) ready.", hostname, version); 541 (void) setjmp(errcatch); 542 for (;;) 543 (void) yyparse(); 544 /* NOTREACHED */ 545} 546 547static void 548lostconn(signo) 549 int signo; 550{ 551 552 if (debug) 553 syslog(LOG_DEBUG, "lost connection"); 554 dologout(1); 555} 556 557#ifdef VIRTUAL_HOSTING 558/* 559 * read in virtual host tables (if they exist) 560 */ 561 562static void 563inithosts() 564{ 565 FILE *fp; 566 char *cp; 567 struct hostent *hp; 568 struct ftphost *hrp, *lhrp; 569 char line[1024]; 570 571 /* 572 * Fill in the default host information 573 */ 574 if (gethostname(line, sizeof(line)) < 0) 575 line[0] = '\0'; 576 if ((hrp = malloc(sizeof(struct ftphost))) == NULL || 577 (hrp->hostname = strdup(line)) == NULL) 578 fatal("Ran out of memory."); 579 memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr); 580 if ((hp = gethostbyname(hrp->hostname)) != NULL) 581 (void) memcpy(&hrp->hostaddr, 582 hp->h_addr_list[0], 583 sizeof(hrp->hostaddr)); 584 hrp->statfile = _PATH_FTPDSTATFILE; 585 hrp->welcome = _PATH_FTPWELCOME; 586 hrp->loginmsg = _PATH_FTPLOGINMESG; 587 hrp->anonuser = "ftp"; 588 hrp->next = NULL; 589 thishost = firsthost = lhrp = hrp; 590 if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { 591 while (fgets(line, sizeof(line), fp) != NULL) { 592 int i; 593 594 if ((cp = strchr(line, '\n')) == NULL) { 595 /* ignore long lines */ 596 while (fgets(line, sizeof(line), fp) != NULL && 597 strchr(line, '\n') == NULL) 598 ; 599 continue; 600 } 601 *cp = '\0'; 602 cp = strtok(line, " \t"); 603 /* skip comments and empty lines */ 604 if (cp == NULL || line[0] == '#') 605 continue; 606 /* first, try a standard gethostbyname() */ 607 if ((hp = gethostbyname(cp)) == NULL) 608 continue; 609 for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { 610 if (memcmp(&hrp->hostaddr, 611 hp->h_addr_list[0], 612 sizeof(hrp->hostaddr)) == 0) 613 break; 614 } 615 if (hrp == NULL) { 616 if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 617 continue; 618 /* defaults */ 619 hrp->statfile = _PATH_FTPDSTATFILE; 620 hrp->welcome = _PATH_FTPWELCOME; 621 hrp->loginmsg = _PATH_FTPLOGINMESG; 622 hrp->anonuser = "ftp"; 623 hrp->next = NULL; 624 lhrp->next = hrp; 625 lhrp = hrp; 626 } 627 (void) memcpy(&hrp->hostaddr, 628 hp->h_addr_list[0], 629 sizeof(hrp->hostaddr)); 630 /* 631 * determine hostname to use. 632 * force defined name if it is a valid alias 633 * otherwise fallback to primary hostname 634 */ 635 if ((hp = gethostbyaddr((char*)&hrp->hostaddr, 636 sizeof(hrp->hostaddr), 637 AF_INET)) != NULL) { 638 if (strcmp(cp, hp->h_name) != 0) { 639 if (hp->h_aliases == NULL) 640 cp = hp->h_name; 641 else { 642 i = 0; 643 while (hp->h_aliases[i] && 644 strcmp(cp, hp->h_aliases[i]) != 0) 645 ++i; 646 if (hp->h_aliases[i] == NULL) 647 cp = hp->h_name; 648 } 649 } 650 } 651 hrp->hostname = strdup(cp); 652 /* ok, now we now peel off the rest */ 653 i = 0; 654 while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) { 655 if (*cp != '-' && (cp = strdup(cp)) != NULL) { 656 switch (i) { 657 case 0: /* anon user permissions */ 658 hrp->anonuser = cp; 659 break; 660 case 1: /* statistics file */ 661 hrp->statfile = cp; 662 break; 663 case 2: /* welcome message */ 664 hrp->welcome = cp; 665 break; 666 case 3: /* login message */ 667 hrp->loginmsg = cp; 668 break; 669 } 670 } 671 ++i; 672 } 673 } 674 (void) fclose(fp); 675 } 676} 677 678static void 679selecthost(a) 680 struct in_addr *a; 681{ 682 struct ftphost *hrp; 683 684 hrp = thishost = firsthost; /* default */ 685 while (hrp != NULL) { 686 if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { 687 thishost = hrp; 688 break; 689 } 690 hrp = hrp->next; 691 } 692 /* setup static variables as appropriate */ 693 hostname = thishost->hostname; 694 ftpuser = thishost->anonuser; 695} 696#endif 697 698/* 699 * Helper function for sgetpwnam(). 700 */ 701static char * 702sgetsave(s) 703 char *s; 704{ 705 char *new = malloc((unsigned) strlen(s) + 1); 706 707 if (new == NULL) { 708 perror_reply(421, "Local resource failure: malloc"); 709 dologout(1); 710 /* NOTREACHED */ 711 } 712 (void) strcpy(new, s); 713 return (new); 714} 715 716/* 717 * Save the result of a getpwnam. Used for USER command, since 718 * the data returned must not be clobbered by any other command 719 * (e.g., globbing). 720 */ 721static struct passwd * 722sgetpwnam(name) 723 char *name; 724{ 725 static struct passwd save; 726 struct passwd *p; 727 728 if ((p = getpwnam(name)) == NULL) 729 return (p); 730 if (save.pw_name) { 731 free(save.pw_name); 732 free(save.pw_passwd); 733 free(save.pw_gecos); 734 free(save.pw_dir); 735 free(save.pw_shell); 736 } 737 save = *p; 738 save.pw_name = sgetsave(p->pw_name); 739 save.pw_passwd = sgetsave(p->pw_passwd); 740 save.pw_gecos = sgetsave(p->pw_gecos); 741 save.pw_dir = sgetsave(p->pw_dir); 742 save.pw_shell = sgetsave(p->pw_shell); 743 return (&save); 744} 745 746static int login_attempts; /* number of failed login attempts */ 747static int askpasswd; /* had user command, ask for passwd */ 748static char curname[10]; /* current USER name */ 749 750/* 751 * USER command. 752 * Sets global passwd pointer pw if named account exists and is acceptable; 753 * sets askpasswd if a PASS command is expected. If logged in previously, 754 * need to reset state. If name is "ftp" or "anonymous", the name is not in 755 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 756 * If account doesn't exist, ask for passwd anyway. Otherwise, check user 757 * requesting login privileges. Disallow anyone who does not have a standard 758 * shell as returned by getusershell(). Disallow anyone mentioned in the file 759 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 760 */ 761void 762user(name) 763 char *name; 764{ 765 char *cp, *shell; 766 767 if (logged_in) { 768 if (guest) { 769 reply(530, "Can't change user from guest login."); 770 return; 771 } else if (dochroot) { 772 reply(530, "Can't change user from chroot user."); 773 return; 774 } 775 end_login(); 776 } 777 778 guest = 0; 779 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 780 if (checkuser(_PATH_FTPUSERS, "ftp") || 781 checkuser(_PATH_FTPUSERS, "anonymous")) 782 reply(530, "User %s access denied.", name); 783#ifdef VIRTUAL_HOSTING 784 else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { 785#else 786 else if ((pw = sgetpwnam("ftp")) != NULL) { 787#endif 788 guest = 1; 789 askpasswd = 1; 790 reply(331, 791 "Guest login ok, send your email address as password."); 792 } else 793 reply(530, "User %s unknown.", name); 794 if (!askpasswd && logging) 795 syslog(LOG_NOTICE, 796 "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 797 return; 798 } 799 if (anon_only != 0) { 800 reply(530, "Sorry, only anonymous ftp allowed."); 801 return; 802 } 803 804 if ((pw = sgetpwnam(name))) { 805 if ((shell = pw->pw_shell) == NULL || *shell == 0) 806 shell = _PATH_BSHELL; 807 while ((cp = getusershell()) != NULL) 808 if (strcmp(cp, shell) == 0) 809 break; 810 endusershell(); 811 812 if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) { 813 reply(530, "User %s access denied.", name); 814 if (logging) 815 syslog(LOG_NOTICE, 816 "FTP LOGIN REFUSED FROM %s, %s", 817 remotehost, name); 818 pw = (struct passwd *) NULL; 819 return; 820 } 821 } 822 if (logging) 823 strncpy(curname, name, sizeof(curname)-1); 824#ifdef SKEY 825 pwok = skeyaccess(name, NULL, remotehost, addr_string); 826 reply(331, "%s", skey_challenge(name, pw, pwok)); 827#else 828 reply(331, "Password required for %s.", name); 829#endif 830 askpasswd = 1; 831 /* 832 * Delay before reading passwd after first failed 833 * attempt to slow down passwd-guessing programs. 834 */ 835 if (login_attempts) 836 sleep((unsigned) login_attempts); 837} 838 839/* 840 * Check if a user is in the file "fname" 841 */ 842static int 843checkuser(fname, name) 844 char *fname; 845 char *name; 846{ 847 FILE *fd; 848 int found = 0; 849 char *p, line[BUFSIZ]; 850 851 if ((fd = fopen(fname, "r")) != NULL) { 852 while (!found && fgets(line, sizeof(line), fd) != NULL) 853 if ((p = strchr(line, '\n')) != NULL) { 854 *p = '\0'; 855 if (line[0] == '#') 856 continue; 857 /* 858 * if first chr is '@', check group membership 859 */ 860 if (line[0] == '@') { 861 int i = 0; 862 struct group *grp; 863 864 if ((grp = getgrnam(line+1)) == NULL) 865 continue; 866 while (!found && grp->gr_mem[i]) 867 found = strcmp(name, 868 grp->gr_mem[i++]) 869 == 0; 870 } 871 /* 872 * Otherwise, just check for username match 873 */ 874 else 875 found = strcmp(line, name) == 0; 876 } 877 (void) fclose(fd); 878 } 879 return (found); 880} 881 882/* 883 * Terminate login as previous user, if any, resetting state; 884 * used when USER command is given or login fails. 885 */ 886static void 887end_login() 888{ 889 890 (void) seteuid((uid_t)0); 891 if (logged_in) 892 ftpd_logwtmp(ttyline, "", ""); 893 pw = NULL; 894#ifdef LOGIN_CAP 895 setusercontext(NULL, getpwuid(0), (uid_t)0, 896 LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); 897#endif 898 logged_in = 0; 899 guest = 0; 900 dochroot = 0; 901} 902 903void 904pass(passwd) 905 char *passwd; 906{ 907 int rval; 908 FILE *fd; 909#ifdef LOGIN_CAP 910 login_cap_t *lc = NULL; 911#endif 912 static char homedir[MAXPATHLEN]; 913 914 if (logged_in || askpasswd == 0) { 915 reply(503, "Login with USER first."); 916 return; 917 } 918 askpasswd = 0; 919 if (!guest) { /* "ftp" is only account allowed no password */ 920 if (pw == NULL) { 921 rval = 1; /* failure below */ 922 goto skip; 923 } 924#if defined(KERBEROS) 925 rval = klogin(pw, "", hostname, passwd); 926 if (rval == 0) 927 goto skip; 928#endif 929#ifdef SKEY 930 rval = strcmp(skey_crypt(passwd, pw->pw_passwd, pw, pwok), 931 pw->pw_passwd); 932 pwok = 0; 933#else 934 rval = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd); 935#endif 936 /* The strcmp does not catch null passwords! */ 937 if (*pw->pw_passwd == '\0' || 938 (pw->pw_expire && time(NULL) >= pw->pw_expire)) 939 rval = 1; /* failure */ 940skip: 941 /* 942 * If rval == 1, the user failed the authentication check 943 * above. If rval == 0, either Kerberos or local authentication 944 * succeeded. 945 */ 946 if (rval) { 947 reply(530, "Login incorrect."); 948 if (logging) 949 syslog(LOG_NOTICE, 950 "FTP LOGIN FAILED FROM %s, %s", 951 remotehost, curname); 952 pw = NULL; 953 if (login_attempts++ >= 5) { 954 syslog(LOG_NOTICE, 955 "repeated login failures from %s", 956 remotehost); 957 exit(0); 958 } 959 return; 960 } 961 } 962 login_attempts = 0; /* this time successful */ 963 if (setegid((gid_t)pw->pw_gid) < 0) { 964 reply(550, "Can't set gid."); 965 return; 966 } 967 /* May be overridden by login.conf */ 968 (void) umask(defumask); 969#ifdef LOGIN_CAP 970 if ((lc = login_getpwclass(pw)) != NULL) { 971 char remote_ip[MAXHOSTNAMELEN]; 972 973 strncpy(remote_ip, inet_ntoa(his_addr.sin_addr), 974 sizeof(remote_ip) - 1); 975 remote_ip[sizeof(remote_ip) - 1] = 0; 976 if (!auth_hostok(lc, remotehost, remote_ip)) { 977 syslog(LOG_INFO|LOG_AUTH, 978 "FTP LOGIN FAILED (HOST) as %s: permission denied.", 979 pw->pw_name); 980 reply(530, "Permission denied.\n"); 981 pw = NULL; 982 return; 983 } 984 if (!auth_timeok(lc, time(NULL))) { 985 reply(530, "Login not available right now.\n"); 986 pw = NULL; 987 return; 988 } 989 } 990 setusercontext(lc, pw, (uid_t)0, 991 LOGIN_SETGROUP|LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); 992#else 993 (void) initgroups(pw->pw_name, pw->pw_gid); 994#endif 995 996 /* open wtmp before chroot */ 997 ftpd_logwtmp(ttyline, pw->pw_name, remotehost); 998 logged_in = 1; 999 1000 if (guest && stats && statfd < 0) 1001#ifdef VIRTUAL_HOSTING 1002 if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) 1003#else 1004 if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) 1005#endif 1006 stats = 0; 1007 1008 dochroot = 1009#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 1010 login_getcapbool(lc, "ftp-chroot", 0) || 1011#endif 1012 checkuser(_PATH_FTPCHROOT, pw->pw_name); 1013 if (guest) { 1014 /* 1015 * We MUST do a chdir() after the chroot. Otherwise 1016 * the old current directory will be accessible as "." 1017 * outside the new root! 1018 */ 1019 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 1020 reply(550, "Can't set guest privileges."); 1021 goto bad; 1022 } 1023 } else if (dochroot) { 1024 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 1025 reply(550, "Can't change root."); 1026 goto bad; 1027 } 1028 } else if (chdir(pw->pw_dir) < 0) { 1029 if (chdir("/") < 0) { 1030 reply(530, "User %s: can't change directory to %s.", 1031 pw->pw_name, pw->pw_dir); 1032 goto bad; 1033 } else 1034 lreply(230, "No directory! Logging in with home=/"); 1035 } 1036 if (seteuid((uid_t)pw->pw_uid) < 0) { 1037 reply(550, "Can't set uid."); 1038 goto bad; 1039 } 1040 1041 /* 1042 * Set home directory so that use of ~ (tilde) works correctly. 1043 */ 1044 if (getcwd(homedir, MAXPATHLEN) != NULL) 1045 setenv("HOME", homedir, 1); 1046 1047 /* 1048 * Display a login message, if it exists. 1049 * N.B. reply(230,) must follow the message. 1050 */ 1051#ifdef VIRTUAL_HOSTING 1052 if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { 1053#else 1054 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 1055#endif 1056 char *cp, line[LINE_MAX]; 1057 1058 while (fgets(line, sizeof(line), fd) != NULL) { 1059 if ((cp = strchr(line, '\n')) != NULL) 1060 *cp = '\0'; 1061 lreply(230, "%s", line); 1062 } 1063 (void) fflush(stdout); 1064 (void) fclose(fd); 1065 } 1066 if (guest) { 1067 if (ident != NULL) 1068 free(ident); 1069 ident = strdup(passwd); 1070 if (ident == NULL) 1071 fatal("Ran out of memory."); 1072 1073 reply(230, "Guest login ok, access restrictions apply."); 1074#ifdef SETPROCTITLE 1075#ifdef VIRTUAL_HOSTING 1076 if (thishost != firsthost) 1077 snprintf(proctitle, sizeof(proctitle), 1078 "%s: anonymous(%s)/%.*s", remotehost, hostname, 1079 sizeof(proctitle) - sizeof(remotehost) - 1080 sizeof(": anonymous/"), passwd); 1081 else 1082#endif 1083 snprintf(proctitle, sizeof(proctitle), 1084 "%s: anonymous/%.*s", remotehost, 1085 sizeof(proctitle) - sizeof(remotehost) - 1086 sizeof(": anonymous/"), passwd); 1087 setproctitle("%s", proctitle); 1088#endif /* SETPROCTITLE */ 1089 if (logging) 1090 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 1091 remotehost, passwd); 1092 } else { 1093 if (dochroot) 1094 reply(230, "User %s logged in, access restrictions apply.", 1095 pw->pw_name); 1096 else 1097 reply(230, "User %s logged in.", pw->pw_name); 1098 1099#ifdef SETPROCTITLE 1100 snprintf(proctitle, sizeof(proctitle), 1101 "%s: %s", remotehost, pw->pw_name); 1102 setproctitle("%s", proctitle); 1103#endif /* SETPROCTITLE */ 1104 if (logging) 1105 syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 1106 remotehost, pw->pw_name); 1107 } 1108#ifdef LOGIN_CAP 1109 login_close(lc); 1110#endif 1111 return; 1112bad: 1113 /* Forget all about it... */ 1114#ifdef LOGIN_CAP 1115 login_close(lc); 1116#endif 1117 end_login(); 1118} 1119 1120void 1121retrieve(cmd, name) 1122 char *cmd, *name; 1123{ 1124 FILE *fin, *dout; 1125 struct stat st; 1126 int (*closefunc) __P((FILE *)); 1127 long start; 1128 1129 if (cmd == 0) { 1130 fin = fopen(name, "r"), closefunc = fclose; 1131 st.st_size = 0; 1132 } else { 1133 char line[BUFSIZ]; 1134 1135 (void) snprintf(line, sizeof(line), cmd, name), name = line; 1136 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 1137 st.st_size = -1; 1138 st.st_blksize = BUFSIZ; 1139 } 1140 if (fin == NULL) { 1141 if (errno != 0) { 1142 perror_reply(550, name); 1143 if (cmd == 0) { 1144 LOGCMD("get", name); 1145 } 1146 } 1147 return; 1148 } 1149 byte_count = -1; 1150 if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { 1151 reply(550, "%s: not a plain file.", name); 1152 goto done; 1153 } 1154 if (restart_point) { 1155 if (type == TYPE_A) { 1156 off_t i, n; 1157 int c; 1158 1159 n = restart_point; 1160 i = 0; 1161 while (i++ < n) { 1162 if ((c=getc(fin)) == EOF) { 1163 perror_reply(550, name); 1164 goto done; 1165 } 1166 if (c == '\n') 1167 i++; 1168 } 1169 } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 1170 perror_reply(550, name); 1171 goto done; 1172 } 1173 } 1174 dout = dataconn(name, st.st_size, "w"); 1175 if (dout == NULL) 1176 goto done; 1177 time(&start); 1178 send_data(fin, dout, st.st_blksize, st.st_size, 1179 restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 1180 if (cmd == 0 && guest && stats) 1181 logxfer(name, st.st_size, start); 1182 (void) fclose(dout); 1183 data = -1; 1184 pdata = -1; 1185done: 1186 if (cmd == 0) 1187 LOGBYTES("get", name, byte_count); 1188 (*closefunc)(fin); 1189} 1190 1191void 1192store(name, mode, unique) 1193 char *name, *mode; 1194 int unique; 1195{ 1196 FILE *fout, *din; 1197 struct stat st; 1198 int (*closefunc) __P((FILE *)); 1199 1200 if ((unique || guest) && stat(name, &st) == 0 && 1201 (name = gunique(name)) == NULL) { 1202 LOGCMD(*mode == 'w' ? "put" : "append", name); 1203 return; 1204 } 1205 1206 if (restart_point) 1207 mode = "r+"; 1208 fout = fopen(name, mode); 1209 closefunc = fclose; 1210 if (fout == NULL) { 1211 perror_reply(553, name); 1212 LOGCMD(*mode == 'w' ? "put" : "append", name); 1213 return; 1214 } 1215 byte_count = -1; 1216 if (restart_point) { 1217 if (type == TYPE_A) { 1218 off_t i, n; 1219 int c; 1220 1221 n = restart_point; 1222 i = 0; 1223 while (i++ < n) { 1224 if ((c=getc(fout)) == EOF) { 1225 perror_reply(550, name); 1226 goto done; 1227 } 1228 if (c == '\n') 1229 i++; 1230 } 1231 /* 1232 * We must do this seek to "current" position 1233 * because we are changing from reading to 1234 * writing. 1235 */ 1236 if (fseek(fout, 0L, L_INCR) < 0) { 1237 perror_reply(550, name); 1238 goto done; 1239 } 1240 } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 1241 perror_reply(550, name); 1242 goto done; 1243 } 1244 } 1245 din = dataconn(name, (off_t)-1, "r"); 1246 if (din == NULL) 1247 goto done; 1248 if (receive_data(din, fout) == 0) { 1249 if (unique) 1250 reply(226, "Transfer complete (unique file name:%s).", 1251 name); 1252 else 1253 reply(226, "Transfer complete."); 1254 } 1255 (void) fclose(din); 1256 data = -1; 1257 pdata = -1; 1258done: 1259 LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 1260 (*closefunc)(fout); 1261} 1262 1263static FILE * 1264getdatasock(mode) 1265 char *mode; 1266{ 1267 int on = 1, s, t, tries; 1268 1269 if (data >= 0) 1270 return (fdopen(data, mode)); 1271 (void) seteuid((uid_t)0); 1272 s = socket(AF_INET, SOCK_STREAM, 0); 1273 if (s < 0) 1274 goto bad; 1275 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 1276 (char *) &on, sizeof(on)) < 0) 1277 goto bad; 1278 /* anchor socket to avoid multi-homing problems */ 1279 data_source.sin_len = sizeof(struct sockaddr_in); 1280 data_source.sin_family = AF_INET; 1281 data_source.sin_addr = ctrl_addr.sin_addr; 1282 for (tries = 1; ; tries++) { 1283 if (bind(s, (struct sockaddr *)&data_source, 1284 sizeof(data_source)) >= 0) 1285 break; 1286 if (errno != EADDRINUSE || tries > 10) 1287 goto bad; 1288 sleep(tries); 1289 } 1290 (void) seteuid((uid_t)pw->pw_uid); 1291#ifdef IP_TOS 1292 on = IPTOS_THROUGHPUT; 1293 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 1294 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 1295#endif 1296#ifdef TCP_NOPUSH 1297 /* 1298 * Turn off push flag to keep sender TCP from sending short packets 1299 * at the boundaries of each write(). Should probably do a SO_SNDBUF 1300 * to set the send buffer size as well, but that may not be desirable 1301 * in heavy-load situations. 1302 */ 1303 on = 1; 1304 if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0) 1305 syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m"); 1306#endif 1307#ifdef SO_SNDBUF 1308 on = 65536; 1309 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0) 1310 syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m"); 1311#endif 1312 1313 return (fdopen(s, mode)); 1314bad: 1315 /* Return the real value of errno (close may change it) */ 1316 t = errno; 1317 (void) seteuid((uid_t)pw->pw_uid); 1318 (void) close(s); 1319 errno = t; 1320 return (NULL); 1321} 1322 1323static FILE * 1324dataconn(name, size, mode) 1325 char *name; 1326 off_t size; 1327 char *mode; 1328{ 1329 char sizebuf[32]; 1330 FILE *file; 1331 int retry = 0, tos; 1332 1333 file_size = size; 1334 byte_count = 0; 1335 if (size != (off_t) -1) 1336 (void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size); 1337 else 1338 *sizebuf = '\0'; 1339 if (pdata >= 0) { 1340 struct sockaddr_in from; 1341 int s, fromlen = sizeof(from); 1342 struct timeval timeout; 1343 fd_set set; 1344 1345 FD_ZERO(&set); 1346 FD_SET(pdata, &set); 1347 1348 timeout.tv_usec = 0; 1349 timeout.tv_sec = 120; 1350 1351 if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 || 1352 (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) { 1353 reply(425, "Can't open data connection."); 1354 (void) close(pdata); 1355 pdata = -1; 1356 return (NULL); 1357 } 1358 (void) close(pdata); 1359 pdata = s; 1360#ifdef IP_TOS 1361 tos = IPTOS_THROUGHPUT; 1362 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 1363 sizeof(int)); 1364#endif 1365 reply(150, "Opening %s mode data connection for '%s'%s.", 1366 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 1367 return (fdopen(pdata, mode)); 1368 } 1369 if (data >= 0) { 1370 reply(125, "Using existing data connection for '%s'%s.", 1371 name, sizebuf); 1372 usedefault = 1; 1373 return (fdopen(data, mode)); 1374 } 1375 if (usedefault) 1376 data_dest = his_addr; 1377 usedefault = 1; 1378 file = getdatasock(mode); 1379 if (file == NULL) { 1380 reply(425, "Can't create data socket (%s,%d): %s.", 1381 inet_ntoa(data_source.sin_addr), 1382 ntohs(data_source.sin_port), strerror(errno)); 1383 return (NULL); 1384 } 1385 data = fileno(file); 1386 while (connect(data, (struct sockaddr *)&data_dest, 1387 sizeof(data_dest)) < 0) { 1388 if (errno == EADDRINUSE && retry < swaitmax) { 1389 sleep((unsigned) swaitint); 1390 retry += swaitint; 1391 continue; 1392 } 1393 perror_reply(425, "Can't build data connection"); 1394 (void) fclose(file); 1395 data = -1; 1396 return (NULL); 1397 } 1398 reply(150, "Opening %s mode data connection for '%s'%s.", 1399 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 1400 return (file); 1401} 1402 1403/* 1404 * Tranfer the contents of "instr" to "outstr" peer using the appropriate 1405 * encapsulation of the data subject to Mode, Structure, and Type. 1406 * 1407 * NB: Form isn't handled. 1408 */ 1409static void 1410send_data(instr, outstr, blksize, filesize, isreg) 1411 FILE *instr, *outstr; 1412 off_t blksize; 1413 off_t filesize; 1414 int isreg; 1415{ 1416 int c, cnt, filefd, netfd; 1417 char *buf, *bp; 1418 size_t len; 1419 1420 transflag++; 1421 if (setjmp(urgcatch)) { 1422 transflag = 0; 1423 return; 1424 } 1425 switch (type) { 1426 1427 case TYPE_A: 1428 while ((c = getc(instr)) != EOF) { 1429 byte_count++; 1430 if (c == '\n') { 1431 if (ferror(outstr)) 1432 goto data_err; 1433 (void) putc('\r', outstr); 1434 } 1435 (void) putc(c, outstr); 1436 } 1437 fflush(outstr); 1438 transflag = 0; 1439 if (ferror(instr)) 1440 goto file_err; 1441 if (ferror(outstr)) 1442 goto data_err; 1443 reply(226, "Transfer complete."); 1444 return; 1445 1446 case TYPE_I: 1447 case TYPE_L: 1448 /* 1449 * isreg is only set if we are not doing restart and we 1450 * are sending a regular file 1451 */ 1452 netfd = fileno(outstr); 1453 filefd = fileno(instr); 1454 1455 if (isreg && filesize < (off_t)16 * 1024 * 1024) { 1456 buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, 1457 (off_t)0); 1458 if (buf == MAP_FAILED) { 1459 syslog(LOG_WARNING, "mmap(%lu): %m", 1460 (unsigned long)filesize); 1461 goto oldway; 1462 } 1463 bp = buf; 1464 len = filesize; 1465 do { 1466 cnt = write(netfd, bp, len); 1467 len -= cnt; 1468 bp += cnt; 1469 if (cnt > 0) byte_count += cnt; 1470 } while(cnt > 0 && len > 0); 1471 1472 transflag = 0; 1473 munmap(buf, (size_t)filesize); 1474 if (cnt < 0) 1475 goto data_err; 1476 reply(226, "Transfer complete."); 1477 return; 1478 } 1479 1480oldway: 1481 if ((buf = malloc((u_int)blksize)) == NULL) { 1482 transflag = 0; 1483 perror_reply(451, "Local resource failure: malloc"); 1484 return; 1485 } 1486 1487 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 1488 write(netfd, buf, cnt) == cnt) 1489 byte_count += cnt; 1490 transflag = 0; 1491 (void)free(buf); 1492 if (cnt != 0) { 1493 if (cnt < 0) 1494 goto file_err; 1495 goto data_err; 1496 } 1497 reply(226, "Transfer complete."); 1498 return; 1499 default: 1500 transflag = 0; 1501 reply(550, "Unimplemented TYPE %d in send_data", type); 1502 return; 1503 } 1504 1505data_err: 1506 transflag = 0; 1507 perror_reply(426, "Data connection"); 1508 return; 1509 1510file_err: 1511 transflag = 0; 1512 perror_reply(551, "Error on input file"); 1513} 1514 1515/* 1516 * Transfer data from peer to "outstr" using the appropriate encapulation of 1517 * the data subject to Mode, Structure, and Type. 1518 * 1519 * N.B.: Form isn't handled. 1520 */ 1521static int 1522receive_data(instr, outstr) 1523 FILE *instr, *outstr; 1524{ 1525 int c; 1526 int cnt, bare_lfs; 1527 char buf[BUFSIZ]; 1528 1529 transflag++; 1530 if (setjmp(urgcatch)) { 1531 transflag = 0; 1532 return (-1); 1533 } 1534 1535 bare_lfs = 0; 1536 1537 switch (type) { 1538 1539 case TYPE_I: 1540 case TYPE_L: 1541 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 1542 if (write(fileno(outstr), buf, cnt) != cnt) 1543 goto file_err; 1544 byte_count += cnt; 1545 } 1546 if (cnt < 0) 1547 goto data_err; 1548 transflag = 0; 1549 return (0); 1550 1551 case TYPE_E: 1552 reply(553, "TYPE E not implemented."); 1553 transflag = 0; 1554 return (-1); 1555 1556 case TYPE_A: 1557 while ((c = getc(instr)) != EOF) { 1558 byte_count++; 1559 if (c == '\n') 1560 bare_lfs++; 1561 while (c == '\r') { 1562 if (ferror(outstr)) 1563 goto data_err; 1564 if ((c = getc(instr)) != '\n') { 1565 (void) putc ('\r', outstr); 1566 if (c == '\0' || c == EOF) 1567 goto contin2; 1568 } 1569 } 1570 (void) putc(c, outstr); 1571 contin2: ; 1572 } 1573 fflush(outstr); 1574 if (ferror(instr)) 1575 goto data_err; 1576 if (ferror(outstr)) 1577 goto file_err; 1578 transflag = 0; 1579 if (bare_lfs) { 1580 lreply(226, 1581 "WARNING! %d bare linefeeds received in ASCII mode", 1582 bare_lfs); 1583 (void)printf(" File may not have transferred correctly.\r\n"); 1584 } 1585 return (0); 1586 default: 1587 reply(550, "Unimplemented TYPE %d in receive_data", type); 1588 transflag = 0; 1589 return (-1); 1590 } 1591 1592data_err: 1593 transflag = 0; 1594 perror_reply(426, "Data Connection"); 1595 return (-1); 1596 1597file_err: 1598 transflag = 0; 1599 perror_reply(452, "Error writing file"); 1600 return (-1); 1601} 1602 1603void 1604statfilecmd(filename) 1605 char *filename; 1606{ 1607 FILE *fin; 1608 int c; 1609 char line[LINE_MAX]; 1610 1611 (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 1612 fin = ftpd_popen(line, "r"); 1613 lreply(211, "status of %s:", filename); 1614 while ((c = getc(fin)) != EOF) { 1615 if (c == '\n') { 1616 if (ferror(stdout)){ 1617 perror_reply(421, "control connection"); 1618 (void) ftpd_pclose(fin); 1619 dologout(1); 1620 /* NOTREACHED */ 1621 } 1622 if (ferror(fin)) { 1623 perror_reply(551, filename); 1624 (void) ftpd_pclose(fin); 1625 return; 1626 } 1627 (void) putc('\r', stdout); 1628 } 1629 (void) putc(c, stdout); 1630 } 1631 (void) ftpd_pclose(fin); 1632 reply(211, "End of Status"); 1633} 1634 1635void 1636statcmd() 1637{ 1638 struct sockaddr_in *sin; 1639 u_char *a, *p; 1640 1641 lreply(211, "%s FTP server status:", hostname, version); 1642 printf(" %s\r\n", version); 1643 printf(" Connected to %s", remotehost); 1644 if (!isdigit(remotehost[0])) 1645 printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 1646 printf("\r\n"); 1647 if (logged_in) { 1648 if (guest) 1649 printf(" Logged in anonymously\r\n"); 1650 else 1651 printf(" Logged in as %s\r\n", pw->pw_name); 1652 } else if (askpasswd) 1653 printf(" Waiting for password\r\n"); 1654 else 1655 printf(" Waiting for user name\r\n"); 1656 printf(" TYPE: %s", typenames[type]); 1657 if (type == TYPE_A || type == TYPE_E) 1658 printf(", FORM: %s", formnames[form]); 1659 if (type == TYPE_L) 1660#if NBBY == 8 1661 printf(" %d", NBBY); 1662#else 1663 printf(" %d", bytesize); /* need definition! */ 1664#endif 1665 printf("; STRUcture: %s; transfer MODE: %s\r\n", 1666 strunames[stru], modenames[mode]); 1667 if (data != -1) 1668 printf(" Data connection open\r\n"); 1669 else if (pdata != -1) { 1670 printf(" in Passive mode"); 1671 sin = &pasv_addr; 1672 goto printaddr; 1673 } else if (usedefault == 0) { 1674 printf(" PORT"); 1675 sin = &data_dest; 1676printaddr: 1677 a = (u_char *) &sin->sin_addr; 1678 p = (u_char *) &sin->sin_port; 1679#define UC(b) (((int) b) & 0xff) 1680 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 1681 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1682#undef UC 1683 } else 1684 printf(" No data connection\r\n"); 1685 reply(211, "End of status"); 1686} 1687 1688void 1689fatal(s) 1690 char *s; 1691{ 1692 1693 reply(451, "Error in server: %s\n", s); 1694 reply(221, "Closing connection due to server error."); 1695 dologout(0); 1696 /* NOTREACHED */ 1697} 1698 1699void 1700#if __STDC__ 1701reply(int n, const char *fmt, ...) 1702#else 1703reply(n, fmt, va_alist) 1704 int n; 1705 char *fmt; 1706 va_dcl 1707#endif 1708{ 1709 va_list ap; 1710#if __STDC__ 1711 va_start(ap, fmt); 1712#else 1713 va_start(ap); 1714#endif 1715 (void)printf("%d ", n); 1716 (void)vprintf(fmt, ap); 1717 (void)printf("\r\n"); 1718 (void)fflush(stdout); 1719 if (debug) { 1720 syslog(LOG_DEBUG, "<--- %d ", n); 1721 vsyslog(LOG_DEBUG, fmt, ap); 1722 } 1723} 1724 1725void 1726#if __STDC__ 1727lreply(int n, const char *fmt, ...) 1728#else 1729lreply(n, fmt, va_alist) 1730 int n; 1731 char *fmt; 1732 va_dcl 1733#endif 1734{ 1735 va_list ap; 1736#if __STDC__ 1737 va_start(ap, fmt); 1738#else 1739 va_start(ap); 1740#endif 1741 (void)printf("%d- ", n); 1742 (void)vprintf(fmt, ap); 1743 (void)printf("\r\n"); 1744 (void)fflush(stdout); 1745 if (debug) { 1746 syslog(LOG_DEBUG, "<--- %d- ", n); 1747 vsyslog(LOG_DEBUG, fmt, ap); 1748 } 1749} 1750 1751static void 1752ack(s) 1753 char *s; 1754{ 1755 1756 reply(250, "%s command successful.", s); 1757} 1758 1759void 1760nack(s) 1761 char *s; 1762{ 1763 1764 reply(502, "%s command not implemented.", s); 1765} 1766 1767/* ARGSUSED */ 1768void 1769yyerror(s) 1770 char *s; 1771{ 1772 char *cp; 1773 1774 if ((cp = strchr(cbuf,'\n'))) 1775 *cp = '\0'; 1776 reply(500, "'%s': command not understood.", cbuf); 1777} 1778 1779void 1780delete(name) 1781 char *name; 1782{ 1783 struct stat st; 1784 1785 LOGCMD("delete", name); 1786 if (stat(name, &st) < 0) { 1787 perror_reply(550, name); 1788 return; 1789 } 1790 if ((st.st_mode&S_IFMT) == S_IFDIR) { 1791 if (rmdir(name) < 0) { 1792 perror_reply(550, name); 1793 return; 1794 } 1795 goto done; 1796 } 1797 if (unlink(name) < 0) { 1798 perror_reply(550, name); 1799 return; 1800 } 1801done: 1802 ack("DELE"); 1803} 1804 1805void 1806cwd(path) 1807 char *path; 1808{ 1809 1810 if (chdir(path) < 0) 1811 perror_reply(550, path); 1812 else 1813 ack("CWD"); 1814} 1815 1816void 1817makedir(name) 1818 char *name; 1819{ 1820 1821 LOGCMD("mkdir", name); 1822 if (mkdir(name, 0777) < 0) 1823 perror_reply(550, name); 1824 else 1825 reply(257, "MKD command successful."); 1826} 1827 1828void 1829removedir(name) 1830 char *name; 1831{ 1832 1833 LOGCMD("rmdir", name); 1834 if (rmdir(name) < 0) 1835 perror_reply(550, name); 1836 else 1837 ack("RMD"); 1838} 1839 1840void 1841pwd() 1842{ 1843 char path[MAXPATHLEN + 1]; 1844 1845 if (getwd(path) == (char *)NULL) 1846 reply(550, "%s.", path); 1847 else 1848 reply(257, "\"%s\" is current directory.", path); 1849} 1850 1851char * 1852renamefrom(name) 1853 char *name; 1854{ 1855 struct stat st; 1856 1857 if (stat(name, &st) < 0) { 1858 perror_reply(550, name); 1859 return ((char *)0); 1860 } 1861 reply(350, "File exists, ready for destination name"); 1862 return (name); 1863} 1864 1865void 1866renamecmd(from, to) 1867 char *from, *to; 1868{ 1869 struct stat st; 1870 1871 LOGCMD2("rename", from, to); 1872 1873 if (guest && (stat(to, &st) == 0)) { 1874 reply(550, "%s: permission denied", to); 1875 return; 1876 } 1877 1878 if (rename(from, to) < 0) 1879 perror_reply(550, "rename"); 1880 else 1881 ack("RNTO"); 1882} 1883 1884static void 1885dolog(sin) 1886 struct sockaddr_in *sin; 1887{ 1888 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 1889 sizeof(struct in_addr), AF_INET); 1890 1891 if (hp) 1892 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); 1893 else 1894 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 1895 sizeof(remotehost)); 1896#ifdef SETPROCTITLE 1897#ifdef VIRTUAL_HOSTING 1898 if (thishost != firsthost) 1899 snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 1900 remotehost, hostname); 1901 else 1902#endif 1903 snprintf(proctitle, sizeof(proctitle), "%s: connected", 1904 remotehost); 1905 setproctitle("%s", proctitle); 1906#endif /* SETPROCTITLE */ 1907 1908 if (logging) { 1909#ifdef VIRTUAL_HOSTING 1910 if (thishost != firsthost) 1911 syslog(LOG_INFO, "connection from %s (to %s)", 1912 remotehost, hostname); 1913 else 1914#endif 1915 syslog(LOG_INFO, "connection from %s (%s)", remotehost, 1916 inet_ntoa(sin->sin_addr)); 1917 } 1918} 1919 1920/* 1921 * Record logout in wtmp file 1922 * and exit with supplied status. 1923 */ 1924void 1925dologout(status) 1926 int status; 1927{ 1928 /* 1929 * Prevent reception of SIGURG from resulting in a resumption 1930 * back to the main program loop. 1931 */ 1932 transflag = 0; 1933 1934 if (logged_in) { 1935 (void) seteuid((uid_t)0); 1936 ftpd_logwtmp(ttyline, "", ""); 1937#if defined(KERBEROS) 1938 if (!notickets && krbtkfile_env) 1939 unlink(krbtkfile_env); 1940#endif 1941 } 1942 /* beware of flushing buffers after a SIGPIPE */ 1943 _exit(status); 1944} 1945 1946static void 1947myoob(signo) 1948 int signo; 1949{ 1950 char *cp; 1951 1952 /* only process if transfer occurring */ 1953 if (!transflag) 1954 return; 1955 cp = tmpline; 1956 if (getline(cp, 7, stdin) == NULL) { 1957 reply(221, "You could at least say goodbye."); 1958 dologout(0); 1959 } 1960 upper(cp); 1961 if (strcmp(cp, "ABOR\r\n") == 0) { 1962 tmpline[0] = '\0'; 1963 reply(426, "Transfer aborted. Data connection closed."); 1964 reply(226, "Abort successful"); 1965 longjmp(urgcatch, 1); 1966 } 1967 if (strcmp(cp, "STAT\r\n") == 0) { 1968 if (file_size != (off_t) -1) 1969 reply(213, "Status: %qd of %qd bytes transferred", 1970 byte_count, file_size); 1971 else 1972 reply(213, "Status: %qd bytes transferred", byte_count); 1973 } 1974} 1975 1976/* 1977 * Note: a response of 425 is not mentioned as a possible response to 1978 * the PASV command in RFC959. However, it has been blessed as 1979 * a legitimate response by Jon Postel in a telephone conversation 1980 * with Rick Adams on 25 Jan 89. 1981 */ 1982void 1983passive() 1984{ 1985 int len; 1986 char *p, *a; 1987 1988 if (pdata >= 0) /* close old port if one set */ 1989 close(pdata); 1990 1991 pdata = socket(AF_INET, SOCK_STREAM, 0); 1992 if (pdata < 0) { 1993 perror_reply(425, "Can't open passive connection"); 1994 return; 1995 } 1996 1997 (void) seteuid((uid_t)0); 1998 1999#ifdef IP_PORTRANGE 2000 { 2001 int on = restricted_data_ports ? IP_PORTRANGE_HIGH 2002 : IP_PORTRANGE_DEFAULT; 2003 2004 if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2005 (char *)&on, sizeof(on)) < 0) 2006 goto pasv_error; 2007 } 2008#endif 2009 2010 pasv_addr = ctrl_addr; 2011 pasv_addr.sin_port = 0; 2012 if (bind(pdata, (struct sockaddr *)&pasv_addr, 2013 sizeof(pasv_addr)) < 0) 2014 goto pasv_error; 2015 2016 (void) seteuid((uid_t)pw->pw_uid); 2017 2018 len = sizeof(pasv_addr); 2019 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 2020 goto pasv_error; 2021 if (listen(pdata, 1) < 0) 2022 goto pasv_error; 2023 a = (char *) &pasv_addr.sin_addr; 2024 p = (char *) &pasv_addr.sin_port; 2025 2026#define UC(b) (((int) b) & 0xff) 2027 2028 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 2029 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 2030 return; 2031 2032pasv_error: 2033 (void) seteuid((uid_t)pw->pw_uid); 2034 (void) close(pdata); 2035 pdata = -1; 2036 perror_reply(425, "Can't open passive connection"); 2037 return; 2038} 2039 2040/* 2041 * Generate unique name for file with basename "local". 2042 * The file named "local" is already known to exist. 2043 * Generates failure reply on error. 2044 */ 2045static char * 2046gunique(local) 2047 char *local; 2048{ 2049 static char new[MAXPATHLEN]; 2050 struct stat st; 2051 int count; 2052 char *cp; 2053 2054 cp = strrchr(local, '/'); 2055 if (cp) 2056 *cp = '\0'; 2057 if (stat(cp ? local : ".", &st) < 0) { 2058 perror_reply(553, cp ? local : "."); 2059 return ((char *) 0); 2060 } 2061 if (cp) 2062 *cp = '/'; 2063 /* -4 is for the .nn<null> we put on the end below */ 2064 (void) snprintf(new, sizeof(new) - 4, "%s", local); 2065 cp = new + strlen(new); 2066 *cp++ = '.'; 2067 for (count = 1; count < 100; count++) { 2068 (void)sprintf(cp, "%d", count); 2069 if (stat(new, &st) < 0) 2070 return (new); 2071 } 2072 reply(452, "Unique file name cannot be created."); 2073 return (NULL); 2074} 2075 2076/* 2077 * Format and send reply containing system error number. 2078 */ 2079void 2080perror_reply(code, string) 2081 int code; 2082 char *string; 2083{ 2084 2085 reply(code, "%s: %s.", string, strerror(errno)); 2086} 2087 2088static char *onefile[] = { 2089 "", 2090 0 2091}; 2092 2093void 2094send_file_list(whichf) 2095 char *whichf; 2096{ 2097 struct stat st; 2098 DIR *dirp = NULL; 2099 struct dirent *dir; 2100 FILE *dout = NULL; 2101 char **dirlist, *dirname; 2102 int simple = 0; 2103 int freeglob = 0; 2104 glob_t gl; 2105 2106 if (strpbrk(whichf, "~{[*?") != NULL) { 2107 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 2108 2109 memset(&gl, 0, sizeof(gl)); 2110 freeglob = 1; 2111 if (glob(whichf, flags, 0, &gl)) { 2112 reply(550, "not found"); 2113 goto out; 2114 } else if (gl.gl_pathc == 0) { 2115 errno = ENOENT; 2116 perror_reply(550, whichf); 2117 goto out; 2118 } 2119 dirlist = gl.gl_pathv; 2120 } else { 2121 onefile[0] = whichf; 2122 dirlist = onefile; 2123 simple = 1; 2124 } 2125 2126 if (setjmp(urgcatch)) { 2127 transflag = 0; 2128 goto out; 2129 } 2130 while ((dirname = *dirlist++)) { 2131 if (stat(dirname, &st) < 0) { 2132 /* 2133 * If user typed "ls -l", etc, and the client 2134 * used NLST, do what the user meant. 2135 */ 2136 if (dirname[0] == '-' && *dirlist == NULL && 2137 transflag == 0) { 2138 retrieve(_PATH_LS " %s", dirname); 2139 goto out; 2140 } 2141 perror_reply(550, whichf); 2142 if (dout != NULL) { 2143 (void) fclose(dout); 2144 transflag = 0; 2145 data = -1; 2146 pdata = -1; 2147 } 2148 goto out; 2149 } 2150 2151 if (S_ISREG(st.st_mode)) { 2152 if (dout == NULL) { 2153 dout = dataconn("file list", (off_t)-1, "w"); 2154 if (dout == NULL) 2155 goto out; 2156 transflag++; 2157 } 2158 fprintf(dout, "%s%s\n", dirname, 2159 type == TYPE_A ? "\r" : ""); 2160 byte_count += strlen(dirname) + 1; 2161 continue; 2162 } else if (!S_ISDIR(st.st_mode)) 2163 continue; 2164 2165 if ((dirp = opendir(dirname)) == NULL) 2166 continue; 2167 2168 while ((dir = readdir(dirp)) != NULL) { 2169 char nbuf[MAXPATHLEN]; 2170 2171 if (dir->d_name[0] == '.' && dir->d_namlen == 1) 2172 continue; 2173 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 2174 dir->d_namlen == 2) 2175 continue; 2176 2177 snprintf(nbuf, sizeof(nbuf), 2178 "%s/%s", dirname, dir->d_name); 2179 2180 /* 2181 * We have to do a stat to insure it's 2182 * not a directory or special file. 2183 */ 2184 if (simple || (stat(nbuf, &st) == 0 && 2185 S_ISREG(st.st_mode))) { 2186 if (dout == NULL) { 2187 dout = dataconn("file list", (off_t)-1, 2188 "w"); 2189 if (dout == NULL) 2190 goto out; 2191 transflag++; 2192 } 2193 if (nbuf[0] == '.' && nbuf[1] == '/') 2194 fprintf(dout, "%s%s\n", &nbuf[2], 2195 type == TYPE_A ? "\r" : ""); 2196 else 2197 fprintf(dout, "%s%s\n", nbuf, 2198 type == TYPE_A ? "\r" : ""); 2199 byte_count += strlen(nbuf) + 1; 2200 } 2201 } 2202 (void) closedir(dirp); 2203 } 2204 2205 if (dout == NULL) 2206 reply(550, "No files found."); 2207 else if (ferror(dout) != 0) 2208 perror_reply(550, "Data connection"); 2209 else 2210 reply(226, "Transfer complete."); 2211 2212 transflag = 0; 2213 if (dout != NULL) 2214 (void) fclose(dout); 2215 data = -1; 2216 pdata = -1; 2217out: 2218 if (freeglob) { 2219 freeglob = 0; 2220 globfree(&gl); 2221 } 2222} 2223 2224void 2225reapchild(signo) 2226 int signo; 2227{ 2228 while (wait3(NULL, WNOHANG, NULL) > 0); 2229} 2230 2231#ifdef OLD_SETPROCTITLE 2232/* 2233 * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 2234 * Warning, since this is usually started from inetd.conf, it often doesn't 2235 * have much of an environment or arglist to overwrite. 2236 */ 2237void 2238#if __STDC__ 2239setproctitle(const char *fmt, ...) 2240#else 2241setproctitle(fmt, va_alist) 2242 char *fmt; 2243 va_dcl 2244#endif 2245{ 2246 int i; 2247 va_list ap; 2248 char *p, *bp, ch; 2249 char buf[LINE_MAX]; 2250 2251#if __STDC__ 2252 va_start(ap, fmt); 2253#else 2254 va_start(ap); 2255#endif 2256 (void)vsnprintf(buf, sizeof(buf), fmt, ap); 2257 2258 /* make ps print our process name */ 2259 p = Argv[0]; 2260 *p++ = '-'; 2261 2262 i = strlen(buf); 2263 if (i > LastArgv - p - 2) { 2264 i = LastArgv - p - 2; 2265 buf[i] = '\0'; 2266 } 2267 bp = buf; 2268 while (ch = *bp++) 2269 if (ch != '\n' && ch != '\r') 2270 *p++ = ch; 2271 while (p < LastArgv) 2272 *p++ = ' '; 2273} 2274#endif /* OLD_SETPROCTITLE */ 2275 2276static void 2277logxfer(name, size, start) 2278 char *name; 2279 long size; 2280 long start; 2281{ 2282 char buf[1024]; 2283 char path[MAXPATHLEN + 1]; 2284 long now; 2285 2286 if (statfd >= 0 && getwd(path) != NULL) { 2287 time(&now); 2288 snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%ld!%ld\n", 2289 ctime(&now)+4, ident, remotehost, 2290 path, name, size, now - start + (now == start)); 2291 write(statfd, buf, strlen(buf)); 2292 } 2293} 2294