11592Srgrimes/* 21592Srgrimes * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 31592Srgrimes * The Regents of the University of California. All rights reserved. 41592Srgrimes * 51592Srgrimes * Redistribution and use in source and binary forms, with or without 61592Srgrimes * modification, are permitted provided that the following conditions 71592Srgrimes * are met: 81592Srgrimes * 1. Redistributions of source code must retain the above copyright 91592Srgrimes * notice, this list of conditions and the following disclaimer. 101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111592Srgrimes * notice, this list of conditions and the following disclaimer in the 121592Srgrimes * documentation and/or other materials provided with the distribution. 13262136Sbrueffer * 3. Neither the name of the University nor the names of its contributors 141592Srgrimes * may be used to endorse or promote products derived from this software 151592Srgrimes * without specific prior written permission. 161592Srgrimes * 171592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271592Srgrimes * SUCH DAMAGE. 281592Srgrimes */ 291592Srgrimes 3017478Smarkm#if 0 311592Srgrimes#ifndef lint 321592Srgrimesstatic char copyright[] = 331592Srgrimes"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 341592Srgrimes The Regents of the University of California. All rights reserved.\n"; 351592Srgrimes#endif /* not lint */ 3617478Smarkm#endif 371592Srgrimes 3831329Scharnier#ifndef lint 3917478Smarkm#if 0 401592Srgrimesstatic char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; 4131329Scharnier#endif 421592Srgrimes#endif /* not lint */ 431592Srgrimes 44137859Syar#include <sys/cdefs.h> 45137859Syar__FBSDID("$FreeBSD: releng/11.0/libexec/ftpd/ftpd.c 301517 2016-06-06 20:00:13Z lidl $"); 46137859Syar 471592Srgrimes/* 481592Srgrimes * FTP server. 491592Srgrimes */ 501592Srgrimes#include <sys/param.h> 511592Srgrimes#include <sys/ioctl.h> 5266907Swollman#include <sys/mman.h> 531592Srgrimes#include <sys/socket.h> 5466907Swollman#include <sys/stat.h> 5566907Swollman#include <sys/time.h> 561592Srgrimes#include <sys/wait.h> 571592Srgrimes 581592Srgrimes#include <netinet/in.h> 591592Srgrimes#include <netinet/in_systm.h> 601592Srgrimes#include <netinet/ip.h> 618240Swollman#include <netinet/tcp.h> 621592Srgrimes 631592Srgrimes#define FTP_NAMES 641592Srgrimes#include <arpa/ftp.h> 651592Srgrimes#include <arpa/inet.h> 661592Srgrimes#include <arpa/telnet.h> 671592Srgrimes 681592Srgrimes#include <ctype.h> 691592Srgrimes#include <dirent.h> 701592Srgrimes#include <err.h> 711592Srgrimes#include <errno.h> 721592Srgrimes#include <fcntl.h> 731592Srgrimes#include <glob.h> 741592Srgrimes#include <limits.h> 751592Srgrimes#include <netdb.h> 761592Srgrimes#include <pwd.h> 7725187Sdavidn#include <grp.h> 7888763Sache#include <opie.h> 791592Srgrimes#include <signal.h> 80132929Syar#include <stdint.h> 811592Srgrimes#include <stdio.h> 821592Srgrimes#include <stdlib.h> 831592Srgrimes#include <string.h> 841592Srgrimes#include <syslog.h> 851592Srgrimes#include <time.h> 861592Srgrimes#include <unistd.h> 8713139Speter#include <libutil.h> 8825101Sdavidn#ifdef LOGIN_CAP 8925101Sdavidn#include <login_cap.h> 9025101Sdavidn#endif 911592Srgrimes 9274874Smarkm#ifdef USE_PAM 9351433Smarkm#include <security/pam_appl.h> 9451433Smarkm#endif 9551433Smarkm 96301241Slidl#ifdef USE_BLACKLIST 97301241Slidl#include "blacklist_client.h" 98301241Slidl#endif 99301241Slidl 1001592Srgrimes#include "pathnames.h" 1011592Srgrimes#include "extern.h" 1021592Srgrimes 1031592Srgrimes#include <stdarg.h> 1041592Srgrimes 10525165Sdavidnstatic char version[] = "Version 6.00LS"; 10625165Sdavidn#undef main 1071592Srgrimes 10856668Sshinunion sockunion ctrl_addr; 10956668Sshinunion sockunion data_source; 11056668Sshinunion sockunion data_dest; 11156668Sshinunion sockunion his_addr; 11256668Sshinunion sockunion pasv_addr; 1131592Srgrimes 11415196Sdgint daemon_mode; 1151592Srgrimesint data; 116109742Syarint dataport; 117110037Syarint hostinfo = 1; /* print host-specific info in messages */ 1181592Srgrimesint logged_in; 1191592Srgrimesstruct passwd *pw; 120110036Syarchar *homedir; 12176096Smarkmint ftpdebug; 1221592Srgrimesint timeout = 900; /* timeout after 15 minutes of inactivity */ 1231592Srgrimesint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 1241592Srgrimesint logging; 1259933Spstint restricted_data_ports = 1; 12617435Spstint paranoid = 1; /* be extra careful about security */ 12720042Storstenbint anon_only = 0; /* Only anonymous ftp allowed */ 128168849Syarint assumeutf8 = 0; /* Assume that server file names are in UTF-8 */ 1291592Srgrimesint guest; 13017435Spstint dochroot; 131137862Syarchar *chrootdir; 132102311Syarint dowtmp = 1; 1336740Sguidoint stats; 1346740Sguidoint statfd = -1; 1351592Srgrimesint type; 1361592Srgrimesint form; 1371592Srgrimesint stru; /* avoid C keyword */ 1381592Srgrimesint mode; 1391592Srgrimesint usedefault = 1; /* for data transfers */ 1401592Srgrimesint pdata = -1; /* for passive mode */ 141137861Syarint readonly = 0; /* Server is in readonly mode. */ 142137861Syarint noepsv = 0; /* EPSV command is disabled. */ 143137861Syarint noretr = 0; /* RETR command is disabled. */ 144137861Syarint noguestretr = 0; /* RETR command is disabled for anon users. */ 145137861Syarint noguestmkd = 0; /* MKD command is disabled for anon users. */ 146137861Syarint noguestmod = 1; /* anon users may not modify existing files. */ 14782460Snik 1481592Srgrimesoff_t file_size; 1491592Srgrimesoff_t byte_count; 1501592Srgrimes#if !defined(CMASK) || CMASK == 0 1511592Srgrimes#undef CMASK 1521592Srgrimes#define CMASK 027 1531592Srgrimes#endif 1541592Srgrimesint defumask = CMASK; /* default umask value */ 1551592Srgrimeschar tmpline[7]; 15627650Sdavidnchar *hostname; 15778153Sddint epsvall = 0; 15878153Sdd 15925283Sdavidn#ifdef VIRTUAL_HOSTING 16025283Sdavidnchar *ftpuser; 16125283Sdavidn 16225283Sdavidnstatic struct ftphost { 16325283Sdavidn struct ftphost *next; 16457124Sshin struct addrinfo *hostinfo; 16525283Sdavidn char *hostname; 16625283Sdavidn char *anonuser; 16725283Sdavidn char *statfile; 16825283Sdavidn char *welcome; 16925283Sdavidn char *loginmsg; 17025283Sdavidn} *thishost, *firsthost; 17125283Sdavidn 17225283Sdavidn#endif 173137983Syarchar remotehost[NI_MAXHOST]; 1746740Sguidochar *ident = NULL; 17517435Spst 176202209Sedstatic char wtmpid[20]; 17717435Spst 17874874Smarkm#ifdef USE_PAM 17990148Simpstatic int auth_pam(struct passwd**, const char*); 180137861Syarpam_handle_t *pamh = NULL; 18188763Sache#endif 18279469Smarkm 183137861Syarstatic struct opie opiedata; 184137861Syarstatic char opieprompt[OPIE_CHALLENGE_MAX+1]; 185137861Syarstatic int pwok; 18617478Smarkm 187154630Syarchar *pid_file = NULL; /* means default location to pidfile(3) */ 18817483Sjulian 1891592Srgrimes/* 19074470Sjlemon * Limit number of pathnames that glob can return. 19174470Sjlemon * A limit of 0 indicates the number of pathnames is unlimited. 19274470Sjlemon */ 19374470Sjlemon#define MAXGLOBARGS 16384 19474470Sjlemon# 19574470Sjlemon 19674470Sjlemon/* 1971592Srgrimes * Timeout intervals for retrying connections 1981592Srgrimes * to hosts that don't accept PORT cmds. This 1991592Srgrimes * is a kludge, but given the problems with TCP... 2001592Srgrimes */ 2011592Srgrimes#define SWAITMAX 90 /* wait at most 90 seconds */ 2021592Srgrimes#define SWAITINT 5 /* interval between retries */ 2031592Srgrimes 2041592Srgrimesint swaitmax = SWAITMAX; 2051592Srgrimesint swaitint = SWAITINT; 2061592Srgrimes 2071592Srgrimes#ifdef SETPROCTITLE 20813139Speter#ifdef OLD_SETPROCTITLE 2091592Srgrimeschar **Argv = NULL; /* pointer to argument vector */ 2101592Srgrimeschar *LastArgv = NULL; /* end of argv */ 21113139Speter#endif /* OLD_SETPROCTITLE */ 2121592Srgrimeschar proctitle[LINE_MAX]; /* initial part of title */ 2131592Srgrimes#endif /* SETPROCTITLE */ 2141592Srgrimes 215137848Syar#define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1) 216137848Syar#define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1) 217137848Syar#define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt)) 2181592Srgrimes 219140472Syarstatic volatile sig_atomic_t recvurg; 220140472Syarstatic int transflag; /* NB: for debugging only */ 221140472Syar 222140472Syar#define STARTXFER flagxfer(1) 223140472Syar#define ENDXFER flagxfer(0) 224140472Syar 225140472Syar#define START_UNSAFE maskurg(1) 226140472Syar#define END_UNSAFE maskurg(0) 227140472Syar 228140472Syar/* It's OK to put an `else' clause after this macro. */ 229140472Syar#define CHECKOOB(action) \ 230140472Syar if (recvurg) { \ 231140472Syar recvurg = 0; \ 232140472Syar if (myoob()) { \ 233140472Syar ENDXFER; \ 234140472Syar action; \ 235140472Syar } \ 236140472Syar } 237140472Syar 23825283Sdavidn#ifdef VIRTUAL_HOSTING 239156156Sumestatic void inithosts(int); 240137861Syarstatic void selecthost(union sockunion *); 24125283Sdavidn#endif 24290148Simpstatic void ack(char *); 24390148Simpstatic void sigurg(int); 244140472Syarstatic void maskurg(int); 245140472Syarstatic void flagxfer(int); 246140472Syarstatic int myoob(void); 247216932Scsjpstatic int checkuser(char *, char *, int, char **, int *); 24890148Simpstatic FILE *dataconn(char *, off_t, char *); 24990148Simpstatic void dolog(struct sockaddr *); 25090148Simpstatic void end_login(void); 25190148Simpstatic FILE *getdatasock(char *); 252101537Syarstatic int guniquefd(char *, char **); 25390148Simpstatic void lostconn(int); 25490148Simpstatic void sigquit(int); 25590148Simpstatic int receive_data(FILE *, FILE *); 256137660Syarstatic int send_data(FILE *, FILE *, size_t, off_t, int); 2571592Srgrimesstatic struct passwd * 25890148Simp sgetpwnam(char *); 25990148Simpstatic char *sgetsave(char *); 26090148Simpstatic void reapchild(int); 261137851Syarstatic void appendf(char **, char *, ...) __printflike(2, 3); 262137848Syarstatic void logcmd(char *, char *, char *, off_t); 26390148Simpstatic void logxfer(char *, off_t, time_t); 264100486Syarstatic char *doublequote(char *); 265120059Sumestatic int *socksetup(int, char *, const char *); 2661592Srgrimes 2671592Srgrimesint 26890148Simpmain(int argc, char *argv[], char **envp) 2691592Srgrimes{ 270141918Sstefanf socklen_t addrlen; 271301517Slidl int ch, on = 1, tos, s = STDIN_FILENO; 2721592Srgrimes char *cp, line[LINE_MAX]; 2731592Srgrimes FILE *fd; 27456668Sshin char *bindname = NULL; 275109742Syar const char *bindport = "ftp"; 27656668Sshin int family = AF_UNSPEC; 27789935Syar struct sigaction sa; 2781592Srgrimes 27936105Sache tzset(); /* in case no timezone database in ~ftp */ 28089935Syar sigemptyset(&sa.sa_mask); 28189935Syar sa.sa_flags = SA_RESTART; 28236105Sache 28313139Speter#ifdef OLD_SETPROCTITLE 2841592Srgrimes /* 2851592Srgrimes * Save start and extent of argv for setproctitle. 2861592Srgrimes */ 2871592Srgrimes Argv = argv; 2881592Srgrimes while (*envp) 2891592Srgrimes envp++; 2901592Srgrimes LastArgv = envp[-1] + strlen(envp[-1]); 29113139Speter#endif /* OLD_SETPROCTITLE */ 2921592Srgrimes 293138747Syar /* 294138747Syar * Prevent diagnostic messages from appearing on stderr. 295138747Syar * We run as a daemon or from inetd; in both cases, there's 296138747Syar * more reason in logging to syslog. 297138747Syar */ 298138747Syar (void) freopen(_PATH_DEVNULL, "w", stderr); 299138747Syar opterr = 0; 3006740Sguido 301138747Syar /* 302138747Syar * LOG_NDELAY sets up the logging connection immediately, 303138747Syar * necessary for anonymous ftp's that chroot and can't do it later. 304138747Syar */ 305138747Syar openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 306138747Syar 307110037Syar while ((ch = getopt(argc, argv, 308168849Syar "468a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) { 3091592Srgrimes switch (ch) { 310100717Syar case '4': 311120059Sume family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; 31215196Sdg break; 31315196Sdg 314100717Syar case '6': 315120059Sume family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; 316100717Syar break; 317100717Syar 318168849Syar case '8': 319168849Syar assumeutf8 = 1; 320168849Syar break; 321168849Syar 322100717Syar case 'a': 323100717Syar bindname = optarg; 324100717Syar break; 325100717Syar 326100717Syar case 'A': 327100717Syar anon_only = 1; 328100717Syar break; 329100717Syar 3301592Srgrimes case 'd': 33176096Smarkm ftpdebug++; 3321592Srgrimes break; 3331592Srgrimes 334100717Syar case 'D': 335100717Syar daemon_mode++; 336100717Syar break; 337100717Syar 33870102Sphk case 'E': 33970102Sphk noepsv = 1; 34070102Sphk break; 34170102Sphk 342110037Syar case 'h': 343110037Syar hostinfo = 0; 344110037Syar break; 345110037Syar 3461592Srgrimes case 'l': 3471592Srgrimes logging++; /* > 1 == extra logging */ 3481592Srgrimes break; 3491592Srgrimes 350101537Syar case 'm': 351101537Syar noguestmod = 0; 352101537Syar break; 353101537Syar 354100717Syar case 'M': 355100717Syar noguestmkd = 1; 356100717Syar break; 357100717Syar 358100717Syar case 'o': 359100717Syar noretr = 1; 360100717Syar break; 361100717Syar 362100717Syar case 'O': 363100717Syar noguestretr = 1; 364100717Syar break; 365100717Syar 366100717Syar case 'p': 367100717Syar pid_file = optarg; 368100717Syar break; 369100717Syar 370109742Syar case 'P': 371109742Syar bindport = optarg; 372109742Syar break; 373109742Syar 37470102Sphk case 'r': 37570102Sphk readonly = 1; 37670102Sphk break; 37770102Sphk 37817435Spst case 'R': 37917435Spst paranoid = 0; 3809933Spst break; 3819933Spst 3826740Sguido case 'S': 38317435Spst stats++; 3846740Sguido break; 38517435Spst 38617435Spst case 't': 38717435Spst timeout = atoi(optarg); 38817435Spst if (maxtimeout < timeout) 38917435Spst maxtimeout = timeout; 39017435Spst break; 39117435Spst 392100717Syar case 'T': 393100717Syar maxtimeout = atoi(optarg); 394100717Syar if (timeout > maxtimeout) 395100717Syar timeout = maxtimeout; 39617435Spst break; 39717435Spst 3981592Srgrimes case 'u': 3991592Srgrimes { 4001592Srgrimes long val = 0; 4011592Srgrimes 4021592Srgrimes val = strtol(optarg, &optarg, 8); 4031592Srgrimes if (*optarg != '\0' || val < 0) 404138747Syar syslog(LOG_WARNING, "bad value for -u"); 4051592Srgrimes else 4061592Srgrimes defumask = val; 4071592Srgrimes break; 4081592Srgrimes } 409100717Syar case 'U': 410100717Syar restricted_data_ports = 0; 41120042Storstenb break; 4121592Srgrimes 4131592Srgrimes case 'v': 414100720Syar ftpdebug++; 4151592Srgrimes break; 4161592Srgrimes 417102311Syar case 'W': 418102311Syar dowtmp = 0; 419102311Syar break; 420102311Syar 4211592Srgrimes default: 422138747Syar syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); 4231592Srgrimes break; 4241592Srgrimes } 4251592Srgrimes } 42615196Sdg 42715196Sdg if (daemon_mode) { 428120059Sume int *ctl_sock, fd, maxfd = -1, nfds, i; 429120059Sume fd_set defreadfds, readfds; 430120059Sume pid_t pid; 431154630Syar struct pidfh *pfh; 43215196Sdg 433154630Syar if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) { 434154630Syar if (errno == EEXIST) { 435154630Syar syslog(LOG_ERR, "%s already running, pid %d", 436154630Syar getprogname(), (int)pid); 437154630Syar exit(1); 438154630Syar } 439154630Syar syslog(LOG_WARNING, "pidfile_open: %m"); 440154630Syar } 441154630Syar 44215196Sdg /* 44315196Sdg * Detach from parent. 44415196Sdg */ 44515196Sdg if (daemon(1, 1) < 0) { 44615196Sdg syslog(LOG_ERR, "failed to become a daemon"); 44715196Sdg exit(1); 44815196Sdg } 449154630Syar 450154630Syar if (pfh != NULL && pidfile_write(pfh) == -1) 451154630Syar syslog(LOG_WARNING, "pidfile_write: %m"); 452154630Syar 45389935Syar sa.sa_handler = reapchild; 45489935Syar (void)sigaction(SIGCHLD, &sa, NULL); 45556668Sshin 456156156Sume#ifdef VIRTUAL_HOSTING 457156156Sume inithosts(family); 458156156Sume#endif 459156156Sume 46015196Sdg /* 46115196Sdg * Open a socket, bind it to the FTP port, and start 46215196Sdg * listening. 46315196Sdg */ 464120059Sume ctl_sock = socksetup(family, bindname, bindport); 465120059Sume if (ctl_sock == NULL) 46615196Sdg exit(1); 467120059Sume 468120059Sume FD_ZERO(&defreadfds); 469120059Sume for (i = 1; i <= *ctl_sock; i++) { 470120059Sume FD_SET(ctl_sock[i], &defreadfds); 471120059Sume if (listen(ctl_sock[i], 32) < 0) { 472120059Sume syslog(LOG_ERR, "control listen: %m"); 473120059Sume exit(1); 474120059Sume } 475120059Sume if (maxfd < ctl_sock[i]) 476120059Sume maxfd = ctl_sock[i]; 47715196Sdg } 478120059Sume 47915196Sdg /* 48015196Sdg * Loop forever accepting connection requests and forking off 48115196Sdg * children to handle them. 48215196Sdg */ 48315196Sdg while (1) { 484120059Sume FD_COPY(&defreadfds, &readfds); 485120059Sume nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); 486120059Sume if (nfds <= 0) { 487120059Sume if (nfds < 0 && errno != EINTR) 488120059Sume syslog(LOG_WARNING, "select: %m"); 489120059Sume continue; 490120059Sume } 491120059Sume 492120059Sume pid = -1; 493120059Sume for (i = 1; i <= *ctl_sock; i++) 494120059Sume if (FD_ISSET(ctl_sock[i], &readfds)) { 495120059Sume addrlen = sizeof(his_addr); 496120059Sume fd = accept(ctl_sock[i], 497120059Sume (struct sockaddr *)&his_addr, 498120059Sume &addrlen); 499154634Syar if (fd == -1) { 500154634Syar syslog(LOG_WARNING, 501154634Syar "accept: %m"); 502154634Syar continue; 503135737Smaxim } 504154634Syar switch (pid = fork()) { 505154634Syar case 0: 506154634Syar /* child */ 507301517Slidl (void) dup2(fd, s); 508301517Slidl (void) dup2(fd, STDOUT_FILENO); 509154634Syar (void) close(fd); 510154634Syar for (i = 1; i <= *ctl_sock; i++) 511154634Syar close(ctl_sock[i]); 512154634Syar if (pfh != NULL) 513154634Syar pidfile_close(pfh); 514154634Syar goto gotchild; 515154634Syar case -1: 516154634Syar syslog(LOG_WARNING, "fork: %m"); 517154634Syar /* FALLTHROUGH */ 518154634Syar default: 519154634Syar close(fd); 520154634Syar } 521120059Sume } 52215196Sdg } 52315196Sdg } else { 52415196Sdg addrlen = sizeof(his_addr); 525301517Slidl if (getpeername(s, (struct sockaddr *)&his_addr, &addrlen) < 0) { 52615196Sdg syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 52715196Sdg exit(1); 52815196Sdg } 529156156Sume 530156156Sume#ifdef VIRTUAL_HOSTING 531156156Sume if (his_addr.su_family == AF_INET6 && 532156156Sume IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 533156156Sume family = AF_INET; 534156156Sume else 535156156Sume family = his_addr.su_family; 536156156Sume inithosts(family); 537156156Sume#endif 53815196Sdg } 53915196Sdg 540154634Syargotchild: 54189935Syar sa.sa_handler = SIG_DFL; 54289935Syar (void)sigaction(SIGCHLD, &sa, NULL); 5431592Srgrimes 54489935Syar sa.sa_handler = sigurg; 54589935Syar sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ 54689935Syar (void)sigaction(SIGURG, &sa, NULL); 54789935Syar 54889935Syar sigfillset(&sa.sa_mask); /* block all signals in handler */ 54989935Syar sa.sa_flags = SA_RESTART; 55089935Syar sa.sa_handler = sigquit; 55189935Syar (void)sigaction(SIGHUP, &sa, NULL); 55289935Syar (void)sigaction(SIGINT, &sa, NULL); 55389935Syar (void)sigaction(SIGQUIT, &sa, NULL); 55489935Syar (void)sigaction(SIGTERM, &sa, NULL); 55589935Syar 55689935Syar sa.sa_handler = lostconn; 55789935Syar (void)sigaction(SIGPIPE, &sa, NULL); 55889935Syar 55915196Sdg addrlen = sizeof(ctrl_addr); 560301517Slidl if (getsockname(s, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 56115196Sdg syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 56215196Sdg exit(1); 56315196Sdg } 564109742Syar dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ 56525283Sdavidn#ifdef VIRTUAL_HOSTING 56625283Sdavidn /* select our identity from virtual host table */ 56756668Sshin selecthost(&ctrl_addr); 56825283Sdavidn#endif 56915196Sdg#ifdef IP_TOS 57056668Sshin if (ctrl_addr.su_family == AF_INET) 57156668Sshin { 57215196Sdg tos = IPTOS_LOWDELAY; 573301517Slidl if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 574100609Syar syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); 57556668Sshin } 57615196Sdg#endif 57735482Sdg /* 57835482Sdg * Disable Nagle on the control channel so that we don't have to wait 57935482Sdg * for peer's ACK before issuing our next reply. 58035482Sdg */ 581301517Slidl if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 582100609Syar syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); 58335482Sdg 58456668Sshin data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); 58515196Sdg 586202209Sed (void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid()); 58717435Spst 5881592Srgrimes /* Try to handle urgent data inline */ 5891592Srgrimes#ifdef SO_OOBINLINE 590301517Slidl if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) 591100609Syar syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); 5921592Srgrimes#endif 5931592Srgrimes 5941592Srgrimes#ifdef F_SETOWN 595301517Slidl if (fcntl(s, F_SETOWN, getpid()) == -1) 5961592Srgrimes syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 5971592Srgrimes#endif 59856668Sshin dolog((struct sockaddr *)&his_addr); 5991592Srgrimes /* 6001592Srgrimes * Set up default state 6011592Srgrimes */ 6021592Srgrimes data = -1; 6031592Srgrimes type = TYPE_A; 6041592Srgrimes form = FORM_N; 6051592Srgrimes stru = STRU_F; 6061592Srgrimes mode = MODE_S; 6071592Srgrimes tmpline[0] = '\0'; 6081592Srgrimes 6091592Srgrimes /* If logins are disabled, print out the message. */ 6101592Srgrimes if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 6111592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6121592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6131592Srgrimes *cp = '\0'; 6141592Srgrimes lreply(530, "%s", line); 6151592Srgrimes } 6161592Srgrimes (void) fflush(stdout); 6171592Srgrimes (void) fclose(fd); 6181592Srgrimes reply(530, "System not available."); 6191592Srgrimes exit(0); 6201592Srgrimes } 62125283Sdavidn#ifdef VIRTUAL_HOSTING 622130428Sobrien fd = fopen(thishost->welcome, "r"); 62325283Sdavidn#else 624130428Sobrien fd = fopen(_PATH_FTPWELCOME, "r"); 62525283Sdavidn#endif 626130428Sobrien if (fd != NULL) { 6271592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6281592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6291592Srgrimes *cp = '\0'; 6301592Srgrimes lreply(220, "%s", line); 6311592Srgrimes } 6321592Srgrimes (void) fflush(stdout); 6331592Srgrimes (void) fclose(fd); 6341592Srgrimes /* reply(220,) must follow */ 6351592Srgrimes } 63625283Sdavidn#ifndef VIRTUAL_HOSTING 63727650Sdavidn if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 63876096Smarkm fatalerror("Ran out of memory."); 639137983Syar if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 640137983Syar hostname[0] = '\0'; 64145422Sbrian hostname[MAXHOSTNAMELEN - 1] = '\0'; 64225283Sdavidn#endif 643110037Syar if (hostinfo) 644110037Syar reply(220, "%s FTP server (%s) ready.", hostname, version); 645110037Syar else 646110037Syar reply(220, "FTP server ready."); 647301241Slidl#ifdef USE_BLACKLIST 648301241Slidl blacklist_init(); 649301241Slidl#endif 6501592Srgrimes for (;;) 6511592Srgrimes (void) yyparse(); 6521592Srgrimes /* NOTREACHED */ 6531592Srgrimes} 6541592Srgrimes 6551592Srgrimesstatic void 65690148Simplostconn(int signo) 6571592Srgrimes{ 6581592Srgrimes 65976096Smarkm if (ftpdebug) 6601592Srgrimes syslog(LOG_DEBUG, "lost connection"); 66131329Scharnier dologout(1); 6621592Srgrimes} 6631592Srgrimes 66489935Syarstatic void 66590148Simpsigquit(int signo) 66689935Syar{ 66789935Syar 66889935Syar syslog(LOG_ERR, "got signal %d", signo); 66989935Syar dologout(1); 67089935Syar} 67189935Syar 67225283Sdavidn#ifdef VIRTUAL_HOSTING 6731592Srgrimes/* 67425283Sdavidn * read in virtual host tables (if they exist) 67525283Sdavidn */ 67625283Sdavidn 67725283Sdavidnstatic void 678156156Sumeinithosts(int family) 67925283Sdavidn{ 680100182Syar int insert; 68199877Syar size_t len; 68225283Sdavidn FILE *fp; 68399877Syar char *cp, *mp, *line; 68499877Syar char *hostname; 685100182Syar char *vhost, *anonuser, *statfile, *welcome, *loginmsg; 68625283Sdavidn struct ftphost *hrp, *lhrp; 68756668Sshin struct addrinfo hints, *res, *ai; 68825283Sdavidn 68925283Sdavidn /* 69025283Sdavidn * Fill in the default host information 69125283Sdavidn */ 69299877Syar if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 69376096Smarkm fatalerror("Ran out of memory."); 694137983Syar if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 69599877Syar hostname[0] = '\0'; 69699877Syar hostname[MAXHOSTNAMELEN - 1] = '\0'; 69799877Syar if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 69899877Syar fatalerror("Ran out of memory."); 69999877Syar hrp->hostname = hostname; 70057124Sshin hrp->hostinfo = NULL; 70156668Sshin 70256668Sshin memset(&hints, 0, sizeof(hints)); 703156156Sume hints.ai_flags = AI_PASSIVE; 704156156Sume hints.ai_family = family; 705156156Sume hints.ai_socktype = SOCK_STREAM; 706102183Syar if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) 70757124Sshin hrp->hostinfo = res; 70825283Sdavidn hrp->statfile = _PATH_FTPDSTATFILE; 70925283Sdavidn hrp->welcome = _PATH_FTPWELCOME; 71025283Sdavidn hrp->loginmsg = _PATH_FTPLOGINMESG; 71125283Sdavidn hrp->anonuser = "ftp"; 71225283Sdavidn hrp->next = NULL; 71325283Sdavidn thishost = firsthost = lhrp = hrp; 71425283Sdavidn if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { 715102474Syar int addrsize, gothost; 71656668Sshin void *addr; 71756668Sshin struct hostent *hp; 71856668Sshin 71999877Syar while ((line = fgetln(fp, &len)) != NULL) { 72056668Sshin int i, hp_error; 72125283Sdavidn 72299877Syar /* skip comments */ 72399877Syar if (line[0] == '#') 72425283Sdavidn continue; 72599877Syar if (line[len - 1] == '\n') { 72699877Syar line[len - 1] = '\0'; 72799877Syar mp = NULL; 72899877Syar } else { 72999877Syar if ((mp = malloc(len + 1)) == NULL) 73099877Syar fatalerror("Ran out of memory."); 73199877Syar memcpy(mp, line, len); 73299877Syar mp[len] = '\0'; 73399877Syar line = mp; 73425283Sdavidn } 73525283Sdavidn cp = strtok(line, " \t"); 73699877Syar /* skip empty lines */ 73799877Syar if (cp == NULL) 73899877Syar goto nextline; 739100182Syar vhost = cp; 74056668Sshin 741100182Syar /* set defaults */ 742100182Syar anonuser = "ftp"; 743100182Syar statfile = _PATH_FTPDSTATFILE; 744100182Syar welcome = _PATH_FTPWELCOME; 745100182Syar loginmsg = _PATH_FTPLOGINMESG; 746100182Syar 747100182Syar /* 748100182Syar * Preparse the line so we can use its info 749100182Syar * for all the addresses associated with 750100182Syar * the virtual host name. 751100182Syar * Field 0, the virtual host name, is special: 752100182Syar * it's already parsed off and will be strdup'ed 753100182Syar * later, after we know its canonical form. 754100182Syar */ 755100182Syar for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) 756100182Syar if (*cp != '-' && (cp = strdup(cp))) 757100182Syar switch (i) { 758100182Syar case 1: /* anon user permissions */ 759100182Syar anonuser = cp; 760100182Syar break; 761100182Syar case 2: /* statistics file */ 762100182Syar statfile = cp; 763100182Syar break; 764100182Syar case 3: /* welcome message */ 765100182Syar welcome = cp; 766100182Syar break; 767100182Syar case 4: /* login message */ 768100182Syar loginmsg = cp; 769100182Syar break; 770100182Syar default: /* programming error */ 771100182Syar abort(); 772100182Syar /* NOTREACHED */ 773100182Syar } 774100182Syar 77556668Sshin hints.ai_flags = AI_PASSIVE; 776156156Sume hints.ai_family = family; 777156156Sume hints.ai_socktype = SOCK_STREAM; 778102183Syar if (getaddrinfo(vhost, NULL, &hints, &res) != 0) 77999877Syar goto nextline; 78056668Sshin for (ai = res; ai != NULL && ai->ai_addr != NULL; 78162100Sdavidn ai = ai->ai_next) { 78256668Sshin 78362100Sdavidn gothost = 0; 78425283Sdavidn for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { 78557124Sshin struct addrinfo *hi; 78657124Sshin 78757124Sshin for (hi = hrp->hostinfo; hi != NULL; 78857124Sshin hi = hi->ai_next) 78957124Sshin if (hi->ai_addrlen == ai->ai_addrlen && 79057124Sshin memcmp(hi->ai_addr, 79157124Sshin ai->ai_addr, 79262100Sdavidn ai->ai_addr->sa_len) == 0) { 79362100Sdavidn gothost++; 79457124Sshin break; 795100183Syar } 79662100Sdavidn if (gothost) 79762100Sdavidn break; 79825283Sdavidn } 79925283Sdavidn if (hrp == NULL) { 80025283Sdavidn if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 80199877Syar goto nextline; 802102183Syar hrp->hostname = NULL; 803100182Syar insert = 1; 804102473Syar } else { 805106754Syar if (hrp->hostinfo && hrp->hostinfo != res) 806102473Syar freeaddrinfo(hrp->hostinfo); 807100182Syar insert = 0; /* host already in the chain */ 808102473Syar } 80957124Sshin hrp->hostinfo = res; 81057124Sshin 81125283Sdavidn /* 81225283Sdavidn * determine hostname to use. 81356668Sshin * force defined name if there is a valid alias 81425283Sdavidn * otherwise fallback to primary hostname 81525283Sdavidn */ 81656668Sshin /* XXX: getaddrinfo() can't do alias check */ 81757124Sshin switch(hrp->hostinfo->ai_family) { 81856668Sshin case AF_INET: 819100259Syar addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; 820100259Syar addrsize = sizeof(struct in_addr); 82156668Sshin break; 82256668Sshin case AF_INET6: 823100259Syar addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; 824100259Syar addrsize = sizeof(struct in6_addr); 82556668Sshin break; 82656668Sshin default: 82756668Sshin /* should not reach here */ 828102473Syar freeaddrinfo(hrp->hostinfo); 829102473Syar if (insert) 830102473Syar free(hrp); /*not in chain, can free*/ 831102473Syar else 832102473Syar hrp->hostinfo = NULL; /*mark as blank*/ 83399877Syar goto nextline; 83456668Sshin /* NOTREACHED */ 83556668Sshin } 836100612Syar if ((hp = getipnodebyaddr(addr, addrsize, 83757124Sshin hrp->hostinfo->ai_family, 83856668Sshin &hp_error)) != NULL) { 839100182Syar if (strcmp(vhost, hp->h_name) != 0) { 84025283Sdavidn if (hp->h_aliases == NULL) 841100182Syar vhost = hp->h_name; 84225283Sdavidn else { 84325283Sdavidn i = 0; 84425283Sdavidn while (hp->h_aliases[i] && 845100182Syar strcmp(vhost, hp->h_aliases[i]) != 0) 84625283Sdavidn ++i; 84725283Sdavidn if (hp->h_aliases[i] == NULL) 848100182Syar vhost = hp->h_name; 84925283Sdavidn } 85025283Sdavidn } 85125283Sdavidn } 852102183Syar if (hrp->hostname && 853102183Syar strcmp(hrp->hostname, vhost) != 0) { 854102183Syar free(hrp->hostname); 855102183Syar hrp->hostname = NULL; 856102183Syar } 857102183Syar if (hrp->hostname == NULL && 858102473Syar (hrp->hostname = strdup(vhost)) == NULL) { 859102473Syar freeaddrinfo(hrp->hostinfo); 860102473Syar hrp->hostinfo = NULL; /* mark as blank */ 861102473Syar if (hp) 862102473Syar freehostent(hp); 863100182Syar goto nextline; 864102473Syar } 865100182Syar hrp->anonuser = anonuser; 866100182Syar hrp->statfile = statfile; 867100182Syar hrp->welcome = welcome; 868100182Syar hrp->loginmsg = loginmsg; 869100182Syar if (insert) { 870100182Syar hrp->next = NULL; 871100182Syar lhrp->next = hrp; 872100182Syar lhrp = hrp; 873100182Syar } 874100263Syar if (hp) 875100263Syar freehostent(hp); 87656668Sshin } 87799877Syarnextline: 87899877Syar if (mp) 87999877Syar free(mp); 88025283Sdavidn } 88125283Sdavidn (void) fclose(fp); 88225283Sdavidn } 88325283Sdavidn} 88425283Sdavidn 88525283Sdavidnstatic void 88690148Simpselecthost(union sockunion *su) 88725283Sdavidn{ 88825283Sdavidn struct ftphost *hrp; 88956668Sshin u_int16_t port; 89056668Sshin#ifdef INET6 89156668Sshin struct in6_addr *mapped_in6 = NULL; 89256668Sshin#endif 89357124Sshin struct addrinfo *hi; 89425283Sdavidn 89556668Sshin#ifdef INET6 89656668Sshin /* 89756668Sshin * XXX IPv4 mapped IPv6 addr consideraton, 89856668Sshin * specified in rfc2373. 89956668Sshin */ 90056668Sshin if (su->su_family == AF_INET6 && 90156668Sshin IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) 90256668Sshin mapped_in6 = &su->su_sin6.sin6_addr; 90356668Sshin#endif 90456668Sshin 90525283Sdavidn hrp = thishost = firsthost; /* default */ 90656668Sshin port = su->su_port; 90756668Sshin su->su_port = 0; 90825283Sdavidn while (hrp != NULL) { 90962100Sdavidn for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { 91057124Sshin if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { 91125283Sdavidn thishost = hrp; 912137987Syar goto found; 91325283Sdavidn } 91456668Sshin#ifdef INET6 91556668Sshin /* XXX IPv4 mapped IPv6 addr consideraton */ 91657124Sshin if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && 91756668Sshin (memcmp(&mapped_in6->s6_addr[12], 91857124Sshin &((struct sockaddr_in *)hi->ai_addr)->sin_addr, 91956668Sshin sizeof(struct in_addr)) == 0)) { 92056668Sshin thishost = hrp; 921137987Syar goto found; 92256668Sshin } 92356668Sshin#endif 92462100Sdavidn } 92562100Sdavidn hrp = hrp->next; 92625283Sdavidn } 927137987Syarfound: 92856668Sshin su->su_port = port; 92925283Sdavidn /* setup static variables as appropriate */ 93025283Sdavidn hostname = thishost->hostname; 93125283Sdavidn ftpuser = thishost->anonuser; 93225283Sdavidn} 93325283Sdavidn#endif 93425283Sdavidn 93525283Sdavidn/* 9361592Srgrimes * Helper function for sgetpwnam(). 9371592Srgrimes */ 9381592Srgrimesstatic char * 93990148Simpsgetsave(char *s) 9401592Srgrimes{ 941137659Syar char *new = malloc(strlen(s) + 1); 9421592Srgrimes 9431592Srgrimes if (new == NULL) { 944137852Syar reply(421, "Ran out of memory."); 9451592Srgrimes dologout(1); 9461592Srgrimes /* NOTREACHED */ 9471592Srgrimes } 9481592Srgrimes (void) strcpy(new, s); 9491592Srgrimes return (new); 9501592Srgrimes} 9511592Srgrimes 9521592Srgrimes/* 9531592Srgrimes * Save the result of a getpwnam. Used for USER command, since 9541592Srgrimes * the data returned must not be clobbered by any other command 9551592Srgrimes * (e.g., globbing). 956137076Syar * NB: The data returned by sgetpwnam() will remain valid until 957137076Syar * the next call to this function. Its difference from getpwnam() 958137076Syar * is that sgetpwnam() is known to be called from ftpd code only. 9591592Srgrimes */ 9601592Srgrimesstatic struct passwd * 96190148Simpsgetpwnam(char *name) 9621592Srgrimes{ 9631592Srgrimes static struct passwd save; 9641592Srgrimes struct passwd *p; 9651592Srgrimes 9661592Srgrimes if ((p = getpwnam(name)) == NULL) 9671592Srgrimes return (p); 9681592Srgrimes if (save.pw_name) { 9691592Srgrimes free(save.pw_name); 9701592Srgrimes free(save.pw_passwd); 971261885Sbrueffer free(save.pw_class); 9721592Srgrimes free(save.pw_gecos); 9731592Srgrimes free(save.pw_dir); 9741592Srgrimes free(save.pw_shell); 9751592Srgrimes } 9761592Srgrimes save = *p; 9771592Srgrimes save.pw_name = sgetsave(p->pw_name); 9781592Srgrimes save.pw_passwd = sgetsave(p->pw_passwd); 979261885Sbrueffer save.pw_class = sgetsave(p->pw_class); 9801592Srgrimes save.pw_gecos = sgetsave(p->pw_gecos); 9811592Srgrimes save.pw_dir = sgetsave(p->pw_dir); 9821592Srgrimes save.pw_shell = sgetsave(p->pw_shell); 9831592Srgrimes return (&save); 9841592Srgrimes} 9851592Srgrimes 9861592Srgrimesstatic int login_attempts; /* number of failed login attempts */ 9871592Srgrimesstatic int askpasswd; /* had user command, ask for passwd */ 98864778Ssheldonhstatic char curname[MAXLOGNAME]; /* current USER name */ 9891592Srgrimes 9901592Srgrimes/* 9911592Srgrimes * USER command. 9921592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable; 9931592Srgrimes * sets askpasswd if a PASS command is expected. If logged in previously, 9941592Srgrimes * need to reset state. If name is "ftp" or "anonymous", the name is not in 9951592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 9961592Srgrimes * If account doesn't exist, ask for passwd anyway. Otherwise, check user 9971592Srgrimes * requesting login privileges. Disallow anyone who does not have a standard 9981592Srgrimes * shell as returned by getusershell(). Disallow anyone mentioned in the file 9991592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 10001592Srgrimes */ 10011592Srgrimesvoid 100290148Simpuser(char *name) 10031592Srgrimes{ 1004216932Scsjp int ecode; 10051592Srgrimes char *cp, *shell; 10061592Srgrimes 10071592Srgrimes if (logged_in) { 10081592Srgrimes if (guest) { 10091592Srgrimes reply(530, "Can't change user from guest login."); 10101592Srgrimes return; 101117435Spst } else if (dochroot) { 101217435Spst reply(530, "Can't change user from chroot user."); 101317435Spst return; 10141592Srgrimes } 10151592Srgrimes end_login(); 10161592Srgrimes } 10171592Srgrimes 10181592Srgrimes guest = 0; 1019130428Sobrien#ifdef VIRTUAL_HOSTING 1020130428Sobrien pw = sgetpwnam(thishost->anonuser); 1021130428Sobrien#else 1022130428Sobrien pw = sgetpwnam("ftp"); 1023130428Sobrien#endif 10241592Srgrimes if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 1025216932Scsjp if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) || 1026216932Scsjp (ecode != 0 && ecode != ENOENT)) 10271592Srgrimes reply(530, "User %s access denied.", name); 1028216932Scsjp else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) || 1029216932Scsjp (ecode != 0 && ecode != ENOENT)) 1030216932Scsjp reply(530, "User %s access denied.", name); 1031130428Sobrien else if (pw != NULL) { 10321592Srgrimes guest = 1; 10331592Srgrimes askpasswd = 1; 10341592Srgrimes reply(331, 10353938Spst "Guest login ok, send your email address as password."); 10361592Srgrimes } else 10371592Srgrimes reply(530, "User %s unknown.", name); 10381592Srgrimes if (!askpasswd && logging) 10391592Srgrimes syslog(LOG_NOTICE, 10401592Srgrimes "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 10411592Srgrimes return; 10421592Srgrimes } 104320042Storstenb if (anon_only != 0) { 104420042Storstenb reply(530, "Sorry, only anonymous ftp allowed."); 104520042Storstenb return; 104620042Storstenb } 104720042Storstenb 104817478Smarkm if ((pw = sgetpwnam(name))) { 10491592Srgrimes if ((shell = pw->pw_shell) == NULL || *shell == 0) 10501592Srgrimes shell = _PATH_BSHELL; 1051124687Scharnier setusershell(); 10521592Srgrimes while ((cp = getusershell()) != NULL) 10531592Srgrimes if (strcmp(cp, shell) == 0) 10541592Srgrimes break; 10551592Srgrimes endusershell(); 10561592Srgrimes 1057216932Scsjp if (cp == NULL || 1058216932Scsjp (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) || 1059216932Scsjp (ecode != 0 && ecode != ENOENT))) { 10601592Srgrimes reply(530, "User %s access denied.", name); 10611592Srgrimes if (logging) 10621592Srgrimes syslog(LOG_NOTICE, 10631592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 10641592Srgrimes remotehost, name); 1065137811Syar pw = NULL; 10661592Srgrimes return; 10671592Srgrimes } 10681592Srgrimes } 10691592Srgrimes if (logging) 10701592Srgrimes strncpy(curname, name, sizeof(curname)-1); 107188763Sache 107288763Sache pwok = 0; 107379469Smarkm#ifdef USE_PAM 107479469Smarkm /* XXX Kluge! The conversation mechanism needs to be fixed. */ 107588763Sache#endif 107688763Sache if (opiechallenge(&opiedata, name, opieprompt) == 0) { 107788763Sache pwok = (pw != NULL) && 107888763Sache opieaccessfile(remotehost) && 107988763Sache opiealways(pw->pw_dir); 108088763Sache reply(331, "Response to %s %s for %s.", 108188763Sache opieprompt, pwok ? "requested" : "required", name); 108288763Sache } else { 108388763Sache pwok = 1; 108484146Sache reply(331, "Password required for %s.", name); 108588763Sache } 10861592Srgrimes askpasswd = 1; 10871592Srgrimes /* 10881592Srgrimes * Delay before reading passwd after first failed 10891592Srgrimes * attempt to slow down passwd-guessing programs. 10901592Srgrimes */ 10911592Srgrimes if (login_attempts) 1092137659Syar sleep(login_attempts); 10931592Srgrimes} 10941592Srgrimes 10951592Srgrimes/* 1096109893Syar * Check if a user is in the file "fname", 1097109893Syar * return a pointer to a malloc'd string with the rest 1098109893Syar * of the matching line in "residue" if not NULL. 10991592Srgrimes */ 11001592Srgrimesstatic int 1101216932Scsjpcheckuser(char *fname, char *name, int pwset, char **residue, int *ecode) 11021592Srgrimes{ 11031592Srgrimes FILE *fd; 11041592Srgrimes int found = 0; 110599877Syar size_t len; 110699877Syar char *line, *mp, *p; 11071592Srgrimes 1108216932Scsjp if (ecode != NULL) 1109216932Scsjp *ecode = 0; 111017435Spst if ((fd = fopen(fname, "r")) != NULL) { 111199877Syar while (!found && (line = fgetln(fd, &len)) != NULL) { 111299877Syar /* skip comments */ 111399877Syar if (line[0] == '#') 111499877Syar continue; 111599877Syar if (line[len - 1] == '\n') { 111699877Syar line[len - 1] = '\0'; 111799877Syar mp = NULL; 111899877Syar } else { 111999877Syar if ((mp = malloc(len + 1)) == NULL) 112099877Syar fatalerror("Ran out of memory."); 112199877Syar memcpy(mp, line, len); 112299877Syar mp[len] = '\0'; 112399877Syar line = mp; 112499877Syar } 112599877Syar /* avoid possible leading and trailing whitespace */ 112699877Syar p = strtok(line, " \t"); 112799877Syar /* skip empty lines */ 112899877Syar if (p == NULL) 112999877Syar goto nextline; 113099877Syar /* 113199877Syar * if first chr is '@', check group membership 113299877Syar */ 113399877Syar if (p[0] == '@') { 113499877Syar int i = 0; 113599877Syar struct group *grp; 113699877Syar 1137109893Syar if (p[1] == '\0') /* single @ matches anyone */ 113899877Syar found = 1; 1139109893Syar else { 1140109893Syar if ((grp = getgrnam(p+1)) == NULL) 1141109893Syar goto nextline; 1142109893Syar /* 1143109893Syar * Check user's default group 1144109893Syar */ 1145109893Syar if (pwset && grp->gr_gid == pw->pw_gid) 1146109893Syar found = 1; 1147109893Syar /* 1148109893Syar * Check supplementary groups 1149109893Syar */ 1150109893Syar while (!found && grp->gr_mem[i]) 1151109893Syar found = strcmp(name, 1152109893Syar grp->gr_mem[i++]) 1153109893Syar == 0; 1154109893Syar } 11551592Srgrimes } 115699877Syar /* 115799877Syar * Otherwise, just check for username match 115899877Syar */ 115999877Syar else 116099877Syar found = strcmp(p, name) == 0; 1161109893Syar /* 1162109893Syar * Save the rest of line to "residue" if matched 1163109893Syar */ 1164109893Syar if (found && residue) { 1165109938Syar if ((p = strtok(NULL, "")) != NULL) 1166109938Syar p += strspn(p, " \t"); 1167109938Syar if (p && *p) { 1168109893Syar if ((*residue = strdup(p)) == NULL) 1169109893Syar fatalerror("Ran out of memory."); 1170109893Syar } else 1171109893Syar *residue = NULL; 1172109893Syar } 117399877Syarnextline: 117499877Syar if (mp) 117599877Syar free(mp); 117699877Syar } 11771592Srgrimes (void) fclose(fd); 1178216932Scsjp } else if (ecode != NULL) 1179216932Scsjp *ecode = errno; 11801592Srgrimes return (found); 11811592Srgrimes} 11821592Srgrimes 11831592Srgrimes/* 11841592Srgrimes * Terminate login as previous user, if any, resetting state; 11851592Srgrimes * used when USER command is given or login fails. 11861592Srgrimes */ 11871592Srgrimesstatic void 118890148Simpend_login(void) 11891592Srgrimes{ 119074874Smarkm#ifdef USE_PAM 119174874Smarkm int e; 119274874Smarkm#endif 11931592Srgrimes 1194132893Syar (void) seteuid(0); 1195202604Sed if (logged_in && dowtmp) 1196202604Sed ftpd_logwtmp(wtmpid, NULL, NULL); 11971592Srgrimes pw = NULL; 119825101Sdavidn#ifdef LOGIN_CAP 1199223434Strasz setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | 1200223434Strasz LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | 1201223434Strasz LOGIN_SETENV)); 120225101Sdavidn#endif 120374874Smarkm#ifdef USE_PAM 1204137078Syar if (pamh) { 1205137078Syar if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 1206137078Syar syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 1207137078Syar if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 1208137078Syar syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 1209137078Syar if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 1210137078Syar syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 1211137078Syar pamh = NULL; 1212137078Syar } 121374874Smarkm#endif 12141592Srgrimes logged_in = 0; 12151592Srgrimes guest = 0; 121617435Spst dochroot = 0; 12171592Srgrimes} 12181592Srgrimes 121974874Smarkm#ifdef USE_PAM 122051433Smarkm 122151433Smarkm/* 122251433Smarkm * the following code is stolen from imap-uw PAM authentication module and 122351433Smarkm * login.c 122451433Smarkm */ 122551433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL) 122651433Smarkm 122751433Smarkmstruct cred_t { 122851433Smarkm const char *uname; /* user name */ 122951433Smarkm const char *pass; /* password */ 123051433Smarkm}; 123151433Smarkmtypedef struct cred_t cred_t; 123251433Smarkm 123351433Smarkmstatic int 123451433Smarkmauth_conv(int num_msg, const struct pam_message **msg, 123551433Smarkm struct pam_response **resp, void *appdata) 123651433Smarkm{ 123751433Smarkm int i; 123851433Smarkm cred_t *cred = (cred_t *) appdata; 123991244Sdes struct pam_response *reply; 124051433Smarkm 124191244Sdes reply = calloc(num_msg, sizeof *reply); 124291244Sdes if (reply == NULL) 124391244Sdes return PAM_BUF_ERR; 124491244Sdes 124551433Smarkm for (i = 0; i < num_msg; i++) { 124651433Smarkm switch (msg[i]->msg_style) { 124751433Smarkm case PAM_PROMPT_ECHO_ON: /* assume want user name */ 124851433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 124951433Smarkm reply[i].resp = COPY_STRING(cred->uname); 125051433Smarkm /* PAM frees resp. */ 125151433Smarkm break; 125251433Smarkm case PAM_PROMPT_ECHO_OFF: /* assume want password */ 125351433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 125451433Smarkm reply[i].resp = COPY_STRING(cred->pass); 125551433Smarkm /* PAM frees resp. */ 125651433Smarkm break; 125751433Smarkm case PAM_TEXT_INFO: 125851433Smarkm case PAM_ERROR_MSG: 125951433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 126051433Smarkm reply[i].resp = NULL; 126151433Smarkm break; 126251433Smarkm default: /* unknown message style */ 126351433Smarkm free(reply); 126451433Smarkm return PAM_CONV_ERR; 126551433Smarkm } 126651433Smarkm } 126751433Smarkm 126851433Smarkm *resp = reply; 126951433Smarkm return PAM_SUCCESS; 127051433Smarkm} 127151433Smarkm 127251433Smarkm/* 127351433Smarkm * Attempt to authenticate the user using PAM. Returns 0 if the user is 127451433Smarkm * authenticated, or 1 if not authenticated. If some sort of PAM system 127551433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 127651433Smarkm * function returns -1. This can be used as an indication that we should 127751433Smarkm * fall back to a different authentication mechanism. 127851433Smarkm */ 127951433Smarkmstatic int 128051433Smarkmauth_pam(struct passwd **ppw, const char *pass) 128151433Smarkm{ 128251433Smarkm const char *tmpl_user; 128351433Smarkm const void *item; 128451433Smarkm int rval; 128551433Smarkm int e; 128651433Smarkm cred_t auth_cred = { (*ppw)->pw_name, pass }; 128751433Smarkm struct pam_conv conv = { &auth_conv, &auth_cred }; 128851433Smarkm 128951433Smarkm e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 129051433Smarkm if (e != PAM_SUCCESS) { 1291137108Syar /* 1292137108Syar * In OpenPAM, it's OK to pass NULL to pam_strerror() 1293137108Syar * if context creation has failed in the first place. 1294137108Syar */ 1295137108Syar syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); 129651433Smarkm return -1; 129751433Smarkm } 129851433Smarkm 129967007Sguido e = pam_set_item(pamh, PAM_RHOST, remotehost); 130067007Sguido if (e != PAM_SUCCESS) { 130167007Sguido syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 130267007Sguido pam_strerror(pamh, e)); 1303137078Syar if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 1304137078Syar syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 1305137078Syar } 1306137078Syar pamh = NULL; 130767007Sguido return -1; 130867007Sguido } 130967007Sguido 131051433Smarkm e = pam_authenticate(pamh, 0); 131151433Smarkm switch (e) { 131251433Smarkm case PAM_SUCCESS: 131351433Smarkm /* 131451433Smarkm * With PAM we support the concept of a "template" 131551433Smarkm * user. The user enters a login name which is 131651433Smarkm * authenticated by PAM, usually via a remote service 131751433Smarkm * such as RADIUS or TACACS+. If authentication 131851433Smarkm * succeeds, a different but related "template" name 131951433Smarkm * is used for setting the credentials, shell, and 132051433Smarkm * home directory. The name the user enters need only 132151433Smarkm * exist on the remote authentication server, but the 132251433Smarkm * template name must be present in the local password 132351433Smarkm * database. 132451433Smarkm * 132551433Smarkm * This is supported by two various mechanisms in the 132651433Smarkm * individual modules. However, from the application's 132751433Smarkm * point of view, the template user is always passed 132851433Smarkm * back as a changed value of the PAM_USER item. 132951433Smarkm */ 133051433Smarkm if ((e = pam_get_item(pamh, PAM_USER, &item)) == 133151433Smarkm PAM_SUCCESS) { 133251433Smarkm tmpl_user = (const char *) item; 133351433Smarkm if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 133451433Smarkm *ppw = getpwnam(tmpl_user); 133551433Smarkm } else 133651433Smarkm syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 133751433Smarkm pam_strerror(pamh, e)); 133851433Smarkm rval = 0; 133951433Smarkm break; 134051433Smarkm 134151433Smarkm case PAM_AUTH_ERR: 134251433Smarkm case PAM_USER_UNKNOWN: 134351433Smarkm case PAM_MAXTRIES: 134451433Smarkm rval = 1; 134551433Smarkm break; 134651433Smarkm 134751433Smarkm default: 134874874Smarkm syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 134951433Smarkm rval = -1; 135051433Smarkm break; 135151433Smarkm } 135251433Smarkm 135374874Smarkm if (rval == 0) { 135474874Smarkm e = pam_acct_mgmt(pamh, 0); 1355137986Syar if (e != PAM_SUCCESS) { 1356137986Syar syslog(LOG_ERR, "pam_acct_mgmt: %s", 1357137986Syar pam_strerror(pamh, e)); 135874874Smarkm rval = 1; 135974874Smarkm } 136051433Smarkm } 136174874Smarkm 136274874Smarkm if (rval != 0) { 136374874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 136474874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 136574874Smarkm } 136674874Smarkm pamh = NULL; 136774874Smarkm } 136851433Smarkm return rval; 136951433Smarkm} 137051433Smarkm 137174874Smarkm#endif /* USE_PAM */ 137251433Smarkm 13731592Srgrimesvoid 137490148Simppass(char *passwd) 13751592Srgrimes{ 1376216932Scsjp int rval, ecode; 13771592Srgrimes FILE *fd; 137825101Sdavidn#ifdef LOGIN_CAP 137925101Sdavidn login_cap_t *lc = NULL; 138025101Sdavidn#endif 138174874Smarkm#ifdef USE_PAM 138274874Smarkm int e; 138374874Smarkm#endif 1384109939Syar char *residue = NULL; 138588763Sache char *xpasswd; 13861592Srgrimes 13871592Srgrimes if (logged_in || askpasswd == 0) { 13881592Srgrimes reply(503, "Login with USER first."); 13891592Srgrimes return; 13901592Srgrimes } 13911592Srgrimes askpasswd = 0; 13921592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 139317435Spst if (pw == NULL) { 139417435Spst rval = 1; /* failure below */ 139517435Spst goto skip; 139617435Spst } 139774874Smarkm#ifdef USE_PAM 139851433Smarkm rval = auth_pam(&pw, passwd); 139989622Sache if (rval >= 0) { 140089622Sache opieunlock(); 140117435Spst goto skip; 140289622Sache } 140389622Sache#endif 140488763Sache if (opieverify(&opiedata, passwd) == 0) 140588763Sache xpasswd = pw->pw_passwd; 140689622Sache else if (pwok) { 140788763Sache xpasswd = crypt(passwd, pw->pw_passwd); 140889622Sache if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 140989622Sache xpasswd = ":"; 141089622Sache } else { 141188763Sache rval = 1; 141288763Sache goto skip; 141388763Sache } 141488763Sache rval = strcmp(pw->pw_passwd, xpasswd); 141589622Sache if (pw->pw_expire && time(NULL) >= pw->pw_expire) 141617435Spst rval = 1; /* failure */ 141717435Spstskip: 141817435Spst /* 141917435Spst * If rval == 1, the user failed the authentication check 142051433Smarkm * above. If rval == 0, either PAM or local authentication 142117435Spst * succeeded. 142217435Spst */ 142317435Spst if (rval) { 14241592Srgrimes reply(530, "Login incorrect."); 1425301241Slidl#ifdef USE_BLACKLIST 1426301517Slidl blacklist_notify(1, STDIN_FILENO, "Login incorrect"); 1427301241Slidl#endif 1428110691Syar if (logging) { 14291592Srgrimes syslog(LOG_NOTICE, 1430110691Syar "FTP LOGIN FAILED FROM %s", 1431110691Syar remotehost); 1432110691Syar syslog(LOG_AUTHPRIV | LOG_NOTICE, 14331592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 14341592Srgrimes remotehost, curname); 1435110691Syar } 14361592Srgrimes pw = NULL; 14371592Srgrimes if (login_attempts++ >= 5) { 14381592Srgrimes syslog(LOG_NOTICE, 14391592Srgrimes "repeated login failures from %s", 14401592Srgrimes remotehost); 14411592Srgrimes exit(0); 14421592Srgrimes } 14431592Srgrimes return; 14441592Srgrimes } 1445301241Slidl#ifdef USE_BLACKLIST 1446301241Slidl else { 1447301517Slidl blacklist_notify(0, STDIN_FILENO, "Login successful"); 1448301241Slidl } 1449301241Slidl#endif 14501592Srgrimes } 14511592Srgrimes login_attempts = 0; /* this time successful */ 1452132894Syar if (setegid(pw->pw_gid) < 0) { 14531592Srgrimes reply(550, "Can't set gid."); 14541592Srgrimes return; 14551592Srgrimes } 145625101Sdavidn /* May be overridden by login.conf */ 145725101Sdavidn (void) umask(defumask); 145825101Sdavidn#ifdef LOGIN_CAP 145925674Sdavidn if ((lc = login_getpwclass(pw)) != NULL) { 1460137983Syar char remote_ip[NI_MAXHOST]; 146125101Sdavidn 1462137983Syar if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 146356668Sshin remote_ip, sizeof(remote_ip) - 1, NULL, 0, 1464137983Syar NI_NUMERICHOST)) 1465137983Syar *remote_ip = 0; 146625101Sdavidn remote_ip[sizeof(remote_ip) - 1] = 0; 146725101Sdavidn if (!auth_hostok(lc, remotehost, remote_ip)) { 146825101Sdavidn syslog(LOG_INFO|LOG_AUTH, 146925101Sdavidn "FTP LOGIN FAILED (HOST) as %s: permission denied.", 147025101Sdavidn pw->pw_name); 1471137849Syar reply(530, "Permission denied."); 147225101Sdavidn pw = NULL; 147325101Sdavidn return; 147425101Sdavidn } 147525101Sdavidn if (!auth_timeok(lc, time(NULL))) { 1476137849Syar reply(530, "Login not available right now."); 147725101Sdavidn pw = NULL; 147825101Sdavidn return; 147925101Sdavidn } 148025101Sdavidn } 1481223434Strasz setusercontext(lc, pw, 0, LOGIN_SETALL & 1482223434Strasz ~(LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV)); 148325101Sdavidn#else 148440310Sdes setlogin(pw->pw_name); 14851592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 148625101Sdavidn#endif 14871592Srgrimes 148874874Smarkm#ifdef USE_PAM 148974874Smarkm if (pamh) { 149074874Smarkm if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 149174874Smarkm syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 149274874Smarkm } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 149374874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 149474874Smarkm } 149574874Smarkm } 149674874Smarkm#endif 149774874Smarkm 1498202209Sed dochroot = 1499216932Scsjp checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode) 1500202209Sed#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 1501202209Sed || login_getcapbool(lc, "ftp-chroot", 0) 1502202209Sed#endif 1503202209Sed ; 1504216932Scsjp /* 1505216932Scsjp * It is possible that checkuser() failed to open the chroot file. 1506216932Scsjp * If this is the case, report that logins are un-available, since we 1507216932Scsjp * have no way of checking whether or not the user should be chrooted. 1508216932Scsjp * We ignore ENOENT since it is not required that this file be present. 1509216932Scsjp */ 1510216932Scsjp if (ecode != 0 && ecode != ENOENT) { 1511216932Scsjp reply(530, "Login not available right now."); 1512216932Scsjp return; 1513216932Scsjp } 1514202209Sed chrootdir = NULL; 1515202209Sed 1516202604Sed /* Disable wtmp logging when chrooting. */ 1517202604Sed if (dochroot || guest) 1518202604Sed dowtmp = 0; 1519202604Sed if (dowtmp) 1520202209Sed ftpd_logwtmp(wtmpid, pw->pw_name, 1521102311Syar (struct sockaddr *)&his_addr); 15221592Srgrimes logged_in = 1; 15231592Srgrimes 152417435Spst if (guest && stats && statfd < 0) 152525283Sdavidn#ifdef VIRTUAL_HOSTING 1526130428Sobrien statfd = open(thishost->statfile, O_WRONLY|O_APPEND); 152725283Sdavidn#else 1528130428Sobrien statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); 152925283Sdavidn#endif 1530130428Sobrien if (statfd < 0) 15316740Sguido stats = 0; 15326740Sguido 1533110036Syar /* 1534110036Syar * For a chrooted local user, 1535110036Syar * a) see whether ftpchroot(5) specifies a chroot directory, 1536110036Syar * b) extract the directory pathname from the line, 1537110036Syar * c) expand it to the absolute pathname if necessary. 1538110036Syar */ 1539110036Syar if (dochroot && residue && 1540117349Syar (chrootdir = strtok(residue, " \t")) != NULL) { 1541117349Syar if (chrootdir[0] != '/') 1542117349Syar asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); 1543117349Syar else 1544137862Syar chrootdir = strdup(chrootdir); /* make it permanent */ 1545110036Syar if (chrootdir == NULL) 1546110036Syar fatalerror("Ran out of memory."); 1547110036Syar } 1548110036Syar if (guest || dochroot) { 15491592Srgrimes /* 1550110036Syar * If no chroot directory set yet, use the login directory. 1551110036Syar * Copy it so it can be modified while pw->pw_dir stays intact. 15521592Srgrimes */ 1553110036Syar if (chrootdir == NULL && 1554110036Syar (chrootdir = strdup(pw->pw_dir)) == NULL) 1555110036Syar fatalerror("Ran out of memory."); 1556110036Syar /* 1557110036Syar * Check for the "/chroot/./home" syntax, 1558110036Syar * separate the chroot and home directory pathnames. 1559110036Syar */ 1560110036Syar if ((homedir = strstr(chrootdir, "/./")) != NULL) { 1561110036Syar *(homedir++) = '\0'; /* wipe '/' */ 1562110036Syar homedir++; /* skip '.' */ 1563110036Syar } else { 1564110036Syar /* 1565110036Syar * We MUST do a chdir() after the chroot. Otherwise 1566110036Syar * the old current directory will be accessible as "." 1567110036Syar * outside the new root! 1568110036Syar */ 1569110036Syar homedir = "/"; 1570109939Syar } 1571110036Syar /* 1572110036Syar * Finally, do chroot() 1573110036Syar */ 1574110036Syar if (chroot(chrootdir) < 0) { 157517435Spst reply(550, "Can't change root."); 157617435Spst goto bad; 157717435Spst } 1578228843Scperciva __FreeBSD_libc_enter_restricted_mode(); 1579110036Syar } else /* real user w/o chroot */ 1580110036Syar homedir = pw->pw_dir; 1581110036Syar /* 1582110036Syar * Set euid *before* doing chdir() so 1583110036Syar * a) the user won't be carried to a directory that he couldn't reach 1584110036Syar * on his own due to no permission to upper path components, 1585110036Syar * b) NFS mounted homedirs w/restrictive permissions will be accessible 1586110036Syar * (uid 0 has no root power over NFS if not mapped explicitly.) 1587110036Syar */ 1588132893Syar if (seteuid(pw->pw_uid) < 0) { 15891592Srgrimes reply(550, "Can't set uid."); 15901592Srgrimes goto bad; 15911592Srgrimes } 1592110036Syar if (chdir(homedir) < 0) { 1593110036Syar if (guest || dochroot) { 1594110036Syar reply(550, "Can't change to base directory."); 1595110036Syar goto bad; 1596110036Syar } else { 1597110036Syar if (chdir("/") < 0) { 1598110036Syar reply(550, "Root is inaccessible."); 1599110036Syar goto bad; 1600110036Syar } 1601137852Syar lreply(230, "No directory! Logging in with home=/."); 1602110036Syar } 1603110036Syar } 16048696Sdg 16051592Srgrimes /* 16061592Srgrimes * Display a login message, if it exists. 16071592Srgrimes * N.B. reply(230,) must follow the message. 16081592Srgrimes */ 160925283Sdavidn#ifdef VIRTUAL_HOSTING 1610130428Sobrien fd = fopen(thishost->loginmsg, "r"); 161125283Sdavidn#else 1612130428Sobrien fd = fopen(_PATH_FTPLOGINMESG, "r"); 161325283Sdavidn#endif 1614130428Sobrien if (fd != NULL) { 16151592Srgrimes char *cp, line[LINE_MAX]; 16161592Srgrimes 16171592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 16181592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 16191592Srgrimes *cp = '\0'; 16201592Srgrimes lreply(230, "%s", line); 16211592Srgrimes } 16221592Srgrimes (void) fflush(stdout); 16231592Srgrimes (void) fclose(fd); 16241592Srgrimes } 16251592Srgrimes if (guest) { 16266740Sguido if (ident != NULL) 16276740Sguido free(ident); 162817433Spst ident = strdup(passwd); 162917433Spst if (ident == NULL) 163076096Smarkm fatalerror("Ran out of memory."); 163117433Spst 16321592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 16331592Srgrimes#ifdef SETPROCTITLE 163425283Sdavidn#ifdef VIRTUAL_HOSTING 163525283Sdavidn if (thishost != firsthost) 163625283Sdavidn snprintf(proctitle, sizeof(proctitle), 163783308Smikeh "%s: anonymous(%s)/%s", remotehost, hostname, 163883308Smikeh passwd); 163925283Sdavidn else 164025283Sdavidn#endif 164125283Sdavidn snprintf(proctitle, sizeof(proctitle), 164283308Smikeh "%s: anonymous/%s", remotehost, passwd); 164313139Speter setproctitle("%s", proctitle); 16441592Srgrimes#endif /* SETPROCTITLE */ 16451592Srgrimes if (logging) 16461592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 16471592Srgrimes remotehost, passwd); 16481592Srgrimes } else { 164984841Syar if (dochroot) 165084841Syar reply(230, "User %s logged in, " 165184841Syar "access restrictions apply.", pw->pw_name); 165284841Syar else 165384841Syar reply(230, "User %s logged in.", pw->pw_name); 165425986Sdanny 16551592Srgrimes#ifdef SETPROCTITLE 16561592Srgrimes snprintf(proctitle, sizeof(proctitle), 165784842Syar "%s: user/%s", remotehost, pw->pw_name); 165813139Speter setproctitle("%s", proctitle); 16591592Srgrimes#endif /* SETPROCTITLE */ 16601592Srgrimes if (logging) 16611592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 16621592Srgrimes remotehost, pw->pw_name); 16631592Srgrimes } 1664140473Syar if (logging && (guest || dochroot)) 1665137985Syar syslog(LOG_INFO, "session root changed to %s", chrootdir); 166625101Sdavidn#ifdef LOGIN_CAP 166725101Sdavidn login_close(lc); 166825101Sdavidn#endif 1669110036Syar if (residue) 1670110036Syar free(residue); 16711592Srgrimes return; 16721592Srgrimesbad: 16731592Srgrimes /* Forget all about it... */ 167425101Sdavidn#ifdef LOGIN_CAP 167525101Sdavidn login_close(lc); 167625101Sdavidn#endif 1677110036Syar if (residue) 1678110036Syar free(residue); 16791592Srgrimes end_login(); 16801592Srgrimes} 16811592Srgrimes 16821592Srgrimesvoid 168390148Simpretrieve(char *cmd, char *name) 16841592Srgrimes{ 16851592Srgrimes FILE *fin, *dout; 16861592Srgrimes struct stat st; 168790148Simp int (*closefunc)(FILE *); 168836612Sjb time_t start; 1689299585Struckman char line[BUFSIZ]; 16901592Srgrimes 16911592Srgrimes if (cmd == 0) { 16921592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 16931592Srgrimes st.st_size = 0; 16941592Srgrimes } else { 1695299585Struckman (void) snprintf(line, sizeof(line), cmd, name); 1696299585Struckman name = line; 16971592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 16981592Srgrimes st.st_size = -1; 16991592Srgrimes st.st_blksize = BUFSIZ; 17001592Srgrimes } 17011592Srgrimes if (fin == NULL) { 17021592Srgrimes if (errno != 0) { 17031592Srgrimes perror_reply(550, name); 17041592Srgrimes if (cmd == 0) { 17051592Srgrimes LOGCMD("get", name); 17061592Srgrimes } 17071592Srgrimes } 17081592Srgrimes return; 17091592Srgrimes } 17101592Srgrimes byte_count = -1; 1711110144Syar if (cmd == 0) { 1712110144Syar if (fstat(fileno(fin), &st) < 0) { 1713110144Syar perror_reply(550, name); 1714110144Syar goto done; 1715110144Syar } 1716110144Syar if (!S_ISREG(st.st_mode)) { 1717125565Syar /* 1718125565Syar * Never sending a raw directory is a workaround 1719125565Syar * for buggy clients that will attempt to RETR 1720125565Syar * a directory before listing it, e.g., Mozilla. 1721125565Syar * Preventing a guest from getting irregular files 1722125565Syar * is a simple security measure. 1723125565Syar */ 1724125565Syar if (S_ISDIR(st.st_mode) || guest) { 1725110144Syar reply(550, "%s: not a plain file.", name); 1726110144Syar goto done; 1727110144Syar } 1728110144Syar st.st_size = -1; 1729110144Syar /* st.st_blksize is set for all descriptor types */ 1730110144Syar } 17311592Srgrimes } 17321592Srgrimes if (restart_point) { 17331592Srgrimes if (type == TYPE_A) { 17341592Srgrimes off_t i, n; 17351592Srgrimes int c; 17361592Srgrimes 17371592Srgrimes n = restart_point; 17381592Srgrimes i = 0; 17391592Srgrimes while (i++ < n) { 17401592Srgrimes if ((c=getc(fin)) == EOF) { 17411592Srgrimes perror_reply(550, name); 17421592Srgrimes goto done; 17431592Srgrimes } 17441592Srgrimes if (c == '\n') 17451592Srgrimes i++; 17461592Srgrimes } 17471592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 17481592Srgrimes perror_reply(550, name); 17491592Srgrimes goto done; 17501592Srgrimes } 17511592Srgrimes } 17521592Srgrimes dout = dataconn(name, st.st_size, "w"); 17531592Srgrimes if (dout == NULL) 17541592Srgrimes goto done; 17556740Sguido time(&start); 17568240Swollman send_data(fin, dout, st.st_blksize, st.st_size, 17578240Swollman restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 1758136929Syar if (cmd == 0 && guest && stats && byte_count > 0) 1759136929Syar logxfer(name, byte_count, start); 17601592Srgrimes (void) fclose(dout); 17611592Srgrimes data = -1; 17621592Srgrimes pdata = -1; 17631592Srgrimesdone: 17641592Srgrimes if (cmd == 0) 17651592Srgrimes LOGBYTES("get", name, byte_count); 17661592Srgrimes (*closefunc)(fin); 17671592Srgrimes} 17681592Srgrimes 17691592Srgrimesvoid 177090148Simpstore(char *name, char *mode, int unique) 17711592Srgrimes{ 1772101537Syar int fd; 17731592Srgrimes FILE *fout, *din; 177490148Simp int (*closefunc)(FILE *); 17751592Srgrimes 1776101537Syar if (*mode == 'a') { /* APPE */ 1777101537Syar if (unique) { 1778101537Syar /* Programming error */ 1779101537Syar syslog(LOG_ERR, "Internal: unique flag to APPE"); 1780101537Syar unique = 0; 1781101537Syar } 1782101537Syar if (guest && noguestmod) { 1783137852Syar reply(550, "Appending to existing file denied."); 1784101537Syar goto err; 1785101537Syar } 1786101537Syar restart_point = 0; /* not affected by preceding REST */ 17871592Srgrimes } 1788101537Syar if (unique) /* STOU overrides REST */ 1789101537Syar restart_point = 0; 1790101537Syar if (guest && noguestmod) { 1791101537Syar if (restart_point) { /* guest STOR w/REST */ 1792137852Syar reply(550, "Modifying existing file denied."); 1793101537Syar goto err; 1794101537Syar } else /* treat guest STOR as STOU */ 1795101537Syar unique = 1; 1796101537Syar } 17971592Srgrimes 17981592Srgrimes if (restart_point) 1799101537Syar mode = "r+"; /* so ASCII manual seek can work */ 1800101537Syar if (unique) { 1801101537Syar if ((fd = guniquefd(name, &name)) < 0) 1802101537Syar goto err; 1803101537Syar fout = fdopen(fd, mode); 1804101537Syar } else 1805101537Syar fout = fopen(name, mode); 18061592Srgrimes closefunc = fclose; 18071592Srgrimes if (fout == NULL) { 18081592Srgrimes perror_reply(553, name); 1809101537Syar goto err; 18101592Srgrimes } 18111592Srgrimes byte_count = -1; 18121592Srgrimes if (restart_point) { 18131592Srgrimes if (type == TYPE_A) { 18141592Srgrimes off_t i, n; 18151592Srgrimes int c; 18161592Srgrimes 18171592Srgrimes n = restart_point; 18181592Srgrimes i = 0; 18191592Srgrimes while (i++ < n) { 18201592Srgrimes if ((c=getc(fout)) == EOF) { 18211592Srgrimes perror_reply(550, name); 18221592Srgrimes goto done; 18231592Srgrimes } 18241592Srgrimes if (c == '\n') 18251592Srgrimes i++; 18261592Srgrimes } 18271592Srgrimes /* 18281592Srgrimes * We must do this seek to "current" position 18291592Srgrimes * because we are changing from reading to 18301592Srgrimes * writing. 18311592Srgrimes */ 1832132930Syar if (fseeko(fout, 0, SEEK_CUR) < 0) { 18331592Srgrimes perror_reply(550, name); 18341592Srgrimes goto done; 18351592Srgrimes } 18361592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 18371592Srgrimes perror_reply(550, name); 18381592Srgrimes goto done; 18391592Srgrimes } 18401592Srgrimes } 1841132930Syar din = dataconn(name, -1, "r"); 18421592Srgrimes if (din == NULL) 18431592Srgrimes goto done; 18441592Srgrimes if (receive_data(din, fout) == 0) { 18451592Srgrimes if (unique) 18461592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 18471592Srgrimes name); 18481592Srgrimes else 18491592Srgrimes reply(226, "Transfer complete."); 18501592Srgrimes } 18511592Srgrimes (void) fclose(din); 18521592Srgrimes data = -1; 18531592Srgrimes pdata = -1; 18541592Srgrimesdone: 1855102566Syar LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); 18561592Srgrimes (*closefunc)(fout); 1857101537Syar return; 1858101537Syarerr: 1859101537Syar LOGCMD(*mode == 'a' ? "append" : "put" , name); 1860101537Syar return; 18611592Srgrimes} 18621592Srgrimes 18631592Srgrimesstatic FILE * 186490148Simpgetdatasock(char *mode) 18651592Srgrimes{ 18661592Srgrimes int on = 1, s, t, tries; 18671592Srgrimes 18681592Srgrimes if (data >= 0) 18691592Srgrimes return (fdopen(data, mode)); 187056668Sshin 187156668Sshin s = socket(data_dest.su_family, SOCK_STREAM, 0); 18721592Srgrimes if (s < 0) 18731592Srgrimes goto bad; 1874100612Syar if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1875100609Syar syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 18761592Srgrimes /* anchor socket to avoid multi-homing problems */ 187756668Sshin data_source = ctrl_addr; 1878109742Syar data_source.su_port = htons(dataport); 1879132893Syar (void) seteuid(0); 18801592Srgrimes for (tries = 1; ; tries++) { 1881132891Syar /* 1882132891Syar * We should loop here since it's possible that 1883132891Syar * another ftpd instance has passed this point and is 1884132891Syar * trying to open a data connection in active mode now. 1885132891Syar * Until the other connection is opened, we'll be getting 1886132891Syar * EADDRINUSE because no SOCK_STREAM sockets in the system 1887132891Syar * can share both local and remote addresses, localIP:20 1888132891Syar * and *:* in this case. 1889132891Syar */ 18901592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 189156668Sshin data_source.su_len) >= 0) 18921592Srgrimes break; 18931592Srgrimes if (errno != EADDRINUSE || tries > 10) 18941592Srgrimes goto bad; 18951592Srgrimes sleep(tries); 18961592Srgrimes } 1897132893Syar (void) seteuid(pw->pw_uid); 18981592Srgrimes#ifdef IP_TOS 189956668Sshin if (data_source.su_family == AF_INET) 190056668Sshin { 19011592Srgrimes on = IPTOS_THROUGHPUT; 1902100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 1903100609Syar syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 190456668Sshin } 19051592Srgrimes#endif 19068240Swollman#ifdef TCP_NOPUSH 19078240Swollman /* 19088240Swollman * Turn off push flag to keep sender TCP from sending short packets 1909166598Syar * at the boundaries of each write(). 19108240Swollman */ 19118240Swollman on = 1; 1912100612Syar if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 1913100609Syar syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 19148240Swollman#endif 19151592Srgrimes return (fdopen(s, mode)); 19161592Srgrimesbad: 19171592Srgrimes /* Return the real value of errno (close may change it) */ 19181592Srgrimes t = errno; 1919132893Syar (void) seteuid(pw->pw_uid); 19201592Srgrimes (void) close(s); 19211592Srgrimes errno = t; 19221592Srgrimes return (NULL); 19231592Srgrimes} 19241592Srgrimes 19251592Srgrimesstatic FILE * 192690148Simpdataconn(char *name, off_t size, char *mode) 19271592Srgrimes{ 19281592Srgrimes char sizebuf[32]; 19291592Srgrimes FILE *file; 1930109611Scjc int retry = 0, tos, conerrno; 19311592Srgrimes 19321592Srgrimes file_size = size; 19331592Srgrimes byte_count = 0; 1934132930Syar if (size != -1) 1935132929Syar (void) snprintf(sizebuf, sizeof(sizebuf), 1936132929Syar " (%jd bytes)", (intmax_t)size); 19371592Srgrimes else 193831973Simp *sizebuf = '\0'; 19391592Srgrimes if (pdata >= 0) { 194056668Sshin union sockunion from; 1941141918Sstefanf socklen_t fromlen = ctrl_addr.su_len; 1942141918Sstefanf int flags, s; 194312532Sguido struct timeval timeout; 194412532Sguido fd_set set; 19451592Srgrimes 194612532Sguido FD_ZERO(&set); 194712532Sguido FD_SET(pdata, &set); 194812532Sguido 194912532Sguido timeout.tv_usec = 0; 195012532Sguido timeout.tv_sec = 120; 195112532Sguido 195286628Syar /* 195386628Syar * Granted a socket is in the blocking I/O mode, 195486628Syar * accept() will block after a successful select() 195586628Syar * if the selected connection dies in between. 195686628Syar * Therefore set the non-blocking I/O flag here. 195786628Syar */ 195886628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 195986628Syar fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 196086628Syar goto pdata_err; 1961132931Syar if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || 196286628Syar (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 196386628Syar goto pdata_err; 19641592Srgrimes (void) close(pdata); 19651592Srgrimes pdata = s; 196686628Syar /* 1967101809Syar * Unset the inherited non-blocking I/O flag 1968101809Syar * on the child socket so stdio can work on it. 196986628Syar */ 197086628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 197186628Syar fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 197286628Syar goto pdata_err; 19731592Srgrimes#ifdef IP_TOS 197456668Sshin if (from.su_family == AF_INET) 197556668Sshin { 197617435Spst tos = IPTOS_THROUGHPUT; 1977100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 1978100609Syar syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 197956668Sshin } 19801592Srgrimes#endif 19811592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19821592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19831592Srgrimes return (fdopen(pdata, mode)); 198486628Syarpdata_err: 198586628Syar reply(425, "Can't open data connection."); 198686628Syar (void) close(pdata); 198786628Syar pdata = -1; 198886628Syar return (NULL); 19891592Srgrimes } 19901592Srgrimes if (data >= 0) { 19911592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 19921592Srgrimes name, sizebuf); 19931592Srgrimes usedefault = 1; 19941592Srgrimes return (fdopen(data, mode)); 19951592Srgrimes } 19961592Srgrimes if (usedefault) 19971592Srgrimes data_dest = his_addr; 19981592Srgrimes usedefault = 1; 1999109611Scjc do { 2000109611Scjc file = getdatasock(mode); 2001109611Scjc if (file == NULL) { 2002137983Syar char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 2003137983Syar 2004137983Syar if (getnameinfo((struct sockaddr *)&data_source, 2005137983Syar data_source.su_len, 2006137983Syar hostbuf, sizeof(hostbuf) - 1, 2007137983Syar portbuf, sizeof(portbuf) - 1, 2008137983Syar NI_NUMERICHOST|NI_NUMERICSERV)) 2009137983Syar *hostbuf = *portbuf = 0; 2010137983Syar hostbuf[sizeof(hostbuf) - 1] = 0; 2011137983Syar portbuf[sizeof(portbuf) - 1] = 0; 2012109611Scjc reply(425, "Can't create data socket (%s,%s): %s.", 2013109611Scjc hostbuf, portbuf, strerror(errno)); 2014109611Scjc return (NULL); 2015109611Scjc } 2016109611Scjc data = fileno(file); 2017109611Scjc conerrno = 0; 2018109611Scjc if (connect(data, (struct sockaddr *)&data_dest, 2019109611Scjc data_dest.su_len) == 0) 2020109611Scjc break; 2021109611Scjc conerrno = errno; 2022109611Scjc (void) fclose(file); 2023109611Scjc data = -1; 2024109611Scjc if (conerrno == EADDRINUSE) { 2025137659Syar sleep(swaitint); 20261592Srgrimes retry += swaitint; 2027109611Scjc } else { 2028109611Scjc break; 20291592Srgrimes } 2030109611Scjc } while (retry <= swaitmax); 2031109611Scjc if (conerrno != 0) { 2032137850Syar reply(425, "Can't build data connection: %s.", 2033137850Syar strerror(conerrno)); 20341592Srgrimes return (NULL); 20351592Srgrimes } 20361592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 20371592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 20381592Srgrimes return (file); 20391592Srgrimes} 20401592Srgrimes 20411592Srgrimes/* 2042140472Syar * A helper macro to avoid code duplication 2043140472Syar * in send_data() and receive_data(). 2044140472Syar * 2045140472Syar * XXX We have to block SIGURG during putc() because BSD stdio 2046140472Syar * is unable to restart interrupted write operations and hence 2047140472Syar * the entire buffer contents will be lost as soon as a write() 2048140472Syar * call indicates EINTR to stdio. 2049140472Syar */ 2050140472Syar#define FTPD_PUTC(ch, file, label) \ 2051140472Syar do { \ 2052140472Syar int ret; \ 2053140472Syar \ 2054140472Syar do { \ 2055140472Syar START_UNSAFE; \ 2056140472Syar ret = putc((ch), (file)); \ 2057140472Syar END_UNSAFE; \ 2058140472Syar CHECKOOB(return (-1)) \ 2059140472Syar else if (ferror(file)) \ 2060140472Syar goto label; \ 2061140472Syar clearerr(file); \ 2062140472Syar } while (ret == EOF); \ 2063140472Syar } while (0) 2064140472Syar 2065140472Syar/* 2066298897Spfg * Transfer the contents of "instr" to "outstr" peer using the appropriate 20678240Swollman * encapsulation of the data subject to Mode, Structure, and Type. 20681592Srgrimes * 20691592Srgrimes * NB: Form isn't handled. 20701592Srgrimes */ 207189935Syarstatic int 2072137660Syarsend_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg) 20731592Srgrimes{ 2074122751Syar int c, cp, filefd, netfd; 207570205Sdan char *buf; 20761592Srgrimes 2077140472Syar STARTXFER; 2078140472Syar 20791592Srgrimes switch (type) { 20801592Srgrimes 20811592Srgrimes case TYPE_A: 2082140472Syar cp = EOF; 2083140472Syar for (;;) { 2084140472Syar c = getc(instr); 2085140472Syar CHECKOOB(return (-1)) 2086140472Syar else if (c == EOF && ferror(instr)) 2087140472Syar goto file_err; 2088140472Syar if (c == EOF) { 2089140472Syar if (ferror(instr)) { /* resume after OOB */ 2090140472Syar clearerr(instr); 2091140472Syar continue; 2092140472Syar } 2093140472Syar if (feof(instr)) /* EOF */ 2094140472Syar break; 2095140472Syar syslog(LOG_ERR, "Internal: impossible condition" 2096140472Syar " on file after getc()"); 2097140472Syar goto file_err; 2098140472Syar } 2099122751Syar if (c == '\n' && cp != '\r') { 2100140472Syar FTPD_PUTC('\r', outstr, data_err); 2101140472Syar byte_count++; 21021592Srgrimes } 2103140472Syar FTPD_PUTC(c, outstr, data_err); 2104140472Syar byte_count++; 2105122751Syar cp = c; 21061592Srgrimes } 2107140472Syar#ifdef notyet /* BSD stdio isn't ready for that */ 2108140472Syar while (fflush(outstr) == EOF) { 2109140472Syar CHECKOOB(return (-1)) 2110140472Syar else 2111140472Syar goto data_err; 2112140472Syar clearerr(outstr); 2113140472Syar } 2114140472Syar ENDXFER; 2115140472Syar#else 2116140472Syar ENDXFER; 2117140472Syar if (fflush(outstr) == EOF) 21181592Srgrimes goto data_err; 2119140472Syar#endif 21201592Srgrimes reply(226, "Transfer complete."); 212189935Syar return (0); 21221592Srgrimes 21231592Srgrimes case TYPE_I: 21241592Srgrimes case TYPE_L: 21258240Swollman /* 21268240Swollman * isreg is only set if we are not doing restart and we 21278240Swollman * are sending a regular file 21288240Swollman */ 21298240Swollman netfd = fileno(outstr); 21308870Srgrimes filefd = fileno(instr); 21318240Swollman 213270205Sdan if (isreg) { 2133136554Syar char *msg = "Transfer complete."; 2134140472Syar off_t cnt, offset; 213570205Sdan int err; 213670205Sdan 2137136555Syar cnt = offset = 0; 213870205Sdan 2139136555Syar while (filesize > 0) { 214090604Smaxim err = sendfile(filefd, netfd, offset, 0, 2141137811Syar NULL, &cnt, 0); 214299212Smaxim /* 214399212Smaxim * Calculate byte_count before OOB processing. 214499212Smaxim * It can be used in myoob() later. 214599212Smaxim */ 214699212Smaxim byte_count += cnt; 214770205Sdan offset += cnt; 214890604Smaxim filesize -= cnt; 2149140472Syar CHECKOOB(return (-1)) 2150140472Syar else if (err == -1) { 2151140472Syar if (errno != EINTR && 2152140472Syar cnt == 0 && offset == 0) 215370205Sdan goto oldway; 215470205Sdan goto data_err; 215570205Sdan } 2156140472Syar if (err == -1) /* resume after OOB */ 2157140472Syar continue; 2158136554Syar /* 2159136554Syar * We hit the EOF prematurely. 2160136554Syar * Perhaps the file was externally truncated. 2161136554Syar */ 2162136554Syar if (cnt == 0) { 2163136554Syar msg = "Transfer finished due to " 2164136554Syar "premature end of file."; 2165136554Syar break; 2166136554Syar } 216770205Sdan } 2168140472Syar ENDXFER; 2169216945Semaste reply(226, "%s", msg); 217089935Syar return (0); 21718240Swollman } 21728240Swollman 21738240Swollmanoldway: 2174137659Syar if ((buf = malloc(blksize)) == NULL) { 2175140472Syar ENDXFER; 2176137852Syar reply(451, "Ran out of memory."); 217789935Syar return (-1); 21781592Srgrimes } 21798870Srgrimes 2180140472Syar for (;;) { 2181140472Syar int cnt, len; 2182140472Syar char *bp; 2183140472Syar 2184140472Syar cnt = read(filefd, buf, blksize); 2185140472Syar CHECKOOB(free(buf); return (-1)) 2186140472Syar else if (cnt < 0) { 2187140472Syar free(buf); 21881592Srgrimes goto file_err; 2189140472Syar } 2190140472Syar if (cnt < 0) /* resume after OOB */ 2191140472Syar continue; 2192140472Syar if (cnt == 0) /* EOF */ 2193140472Syar break; 2194140472Syar for (len = cnt, bp = buf; len > 0;) { 2195140472Syar cnt = write(netfd, bp, len); 2196140472Syar CHECKOOB(free(buf); return (-1)) 2197140472Syar else if (cnt < 0) { 2198140472Syar free(buf); 2199140472Syar goto data_err; 2200140472Syar } 2201140472Syar if (cnt <= 0) 2202140472Syar continue; 2203140472Syar len -= cnt; 2204140472Syar bp += cnt; 2205140472Syar byte_count += cnt; 2206140472Syar } 22071592Srgrimes } 2208140472Syar ENDXFER; 2209140472Syar free(buf); 22101592Srgrimes reply(226, "Transfer complete."); 221189935Syar return (0); 22121592Srgrimes default: 2213140472Syar ENDXFER; 2214137852Syar reply(550, "Unimplemented TYPE %d in send_data.", type); 221589935Syar return (-1); 22161592Srgrimes } 22171592Srgrimes 22181592Srgrimesdata_err: 2219140472Syar ENDXFER; 22201592Srgrimes perror_reply(426, "Data connection"); 222189935Syar return (-1); 22221592Srgrimes 22231592Srgrimesfile_err: 2224140472Syar ENDXFER; 22251592Srgrimes perror_reply(551, "Error on input file"); 222689935Syar return (-1); 22271592Srgrimes} 22281592Srgrimes 22291592Srgrimes/* 22301592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 22311592Srgrimes * the data subject to Mode, Structure, and Type. 22321592Srgrimes * 22331592Srgrimes * N.B.: Form isn't handled. 22341592Srgrimes */ 22351592Srgrimesstatic int 223690148Simpreceive_data(FILE *instr, FILE *outstr) 22371592Srgrimes{ 2238140472Syar int c, cp; 2239140472Syar int bare_lfs = 0; 22401592Srgrimes 2241140472Syar STARTXFER; 224217433Spst 22431592Srgrimes switch (type) { 22441592Srgrimes 22451592Srgrimes case TYPE_I: 22461592Srgrimes case TYPE_L: 2247140472Syar for (;;) { 2248140472Syar int cnt, len; 2249140472Syar char *bp; 2250140472Syar char buf[BUFSIZ]; 2251140472Syar 2252140472Syar cnt = read(fileno(instr), buf, sizeof(buf)); 2253140472Syar CHECKOOB(return (-1)) 2254140472Syar else if (cnt < 0) 2255140472Syar goto data_err; 2256140472Syar if (cnt < 0) /* resume after OOB */ 2257140472Syar continue; 2258140472Syar if (cnt == 0) /* EOF */ 2259140472Syar break; 2260140472Syar for (len = cnt, bp = buf; len > 0;) { 2261140472Syar cnt = write(fileno(outstr), bp, len); 2262140472Syar CHECKOOB(return (-1)) 2263140472Syar else if (cnt < 0) 2264140472Syar goto file_err; 2265140472Syar if (cnt <= 0) 2266140472Syar continue; 2267140472Syar len -= cnt; 2268140472Syar bp += cnt; 2269140472Syar byte_count += cnt; 2270140472Syar } 22711592Srgrimes } 2272140472Syar ENDXFER; 22731592Srgrimes return (0); 22741592Srgrimes 22751592Srgrimes case TYPE_E: 2276140472Syar ENDXFER; 22771592Srgrimes reply(553, "TYPE E not implemented."); 22781592Srgrimes return (-1); 22791592Srgrimes 22801592Srgrimes case TYPE_A: 2281140472Syar cp = EOF; 2282140472Syar for (;;) { 2283140472Syar c = getc(instr); 2284140472Syar CHECKOOB(return (-1)) 2285140472Syar else if (c == EOF && ferror(instr)) 2286140472Syar goto data_err; 2287140472Syar if (c == EOF && ferror(instr)) { /* resume after OOB */ 2288140472Syar clearerr(instr); 2289140472Syar continue; 2290140472Syar } 2291140472Syar 2292140472Syar if (cp == '\r') { 2293140472Syar if (c != '\n') 2294140472Syar FTPD_PUTC('\r', outstr, file_err); 2295140472Syar } else 2296140472Syar if (c == '\n') 2297140472Syar bare_lfs++; 2298140472Syar if (c == '\r') { 2299140472Syar byte_count++; 2300140472Syar cp = c; 2301140472Syar continue; 2302140472Syar } 2303140472Syar 2304140472Syar /* Check for EOF here in order not to lose last \r. */ 2305140472Syar if (c == EOF) { 2306140472Syar if (feof(instr)) /* EOF */ 2307140472Syar break; 2308140472Syar syslog(LOG_ERR, "Internal: impossible condition" 2309140472Syar " on data stream after getc()"); 2310140472Syar goto data_err; 2311140472Syar } 2312140472Syar 23131592Srgrimes byte_count++; 2314140472Syar FTPD_PUTC(c, outstr, file_err); 2315140472Syar cp = c; 23161592Srgrimes } 2317140472Syar#ifdef notyet /* BSD stdio isn't ready for that */ 2318140472Syar while (fflush(outstr) == EOF) { 2319140472Syar CHECKOOB(return (-1)) 2320140472Syar else 2321140472Syar goto file_err; 2322140472Syar clearerr(outstr); 2323140472Syar } 2324140472Syar ENDXFER; 2325140472Syar#else 2326140472Syar ENDXFER; 2327140472Syar if (fflush(outstr) == EOF) 23281592Srgrimes goto file_err; 2329140472Syar#endif 23301592Srgrimes if (bare_lfs) { 23311592Srgrimes lreply(226, 2332137852Syar "WARNING! %d bare linefeeds received in ASCII mode.", 23331592Srgrimes bare_lfs); 23341592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 23351592Srgrimes } 23361592Srgrimes return (0); 23371592Srgrimes default: 2338140472Syar ENDXFER; 2339137852Syar reply(550, "Unimplemented TYPE %d in receive_data.", type); 23401592Srgrimes return (-1); 23411592Srgrimes } 23421592Srgrimes 23431592Srgrimesdata_err: 2344140472Syar ENDXFER; 2345137852Syar perror_reply(426, "Data connection"); 23461592Srgrimes return (-1); 23471592Srgrimes 23481592Srgrimesfile_err: 2349140472Syar ENDXFER; 2350137852Syar perror_reply(452, "Error writing to file"); 23511592Srgrimes return (-1); 23521592Srgrimes} 23531592Srgrimes 23541592Srgrimesvoid 235590148Simpstatfilecmd(char *filename) 23561592Srgrimes{ 23571592Srgrimes FILE *fin; 2358109382Syar int atstart; 2359137728Syar int c, code; 23601592Srgrimes char line[LINE_MAX]; 2361137728Syar struct stat st; 23621592Srgrimes 2363137728Syar code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; 236425165Sdavidn (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 23651592Srgrimes fin = ftpd_popen(line, "r"); 2366216943Semaste if (fin == NULL) { 2367216943Semaste perror_reply(551, filename); 2368216943Semaste return; 2369216943Semaste } 2370137729Syar lreply(code, "Status of %s:", filename); 2371109382Syar atstart = 1; 23721592Srgrimes while ((c = getc(fin)) != EOF) { 23731592Srgrimes if (c == '\n') { 23741592Srgrimes if (ferror(stdout)){ 2375137852Syar perror_reply(421, "Control connection"); 23761592Srgrimes (void) ftpd_pclose(fin); 23771592Srgrimes dologout(1); 23781592Srgrimes /* NOTREACHED */ 23791592Srgrimes } 23801592Srgrimes if (ferror(fin)) { 23811592Srgrimes perror_reply(551, filename); 23821592Srgrimes (void) ftpd_pclose(fin); 23831592Srgrimes return; 23841592Srgrimes } 23851592Srgrimes (void) putc('\r', stdout); 23861592Srgrimes } 2387109382Syar /* 2388109382Syar * RFC 959 says neutral text should be prepended before 2389109382Syar * a leading 3-digit number followed by whitespace, but 2390109382Syar * many ftp clients can be confused by any leading digits, 2391109382Syar * as a matter of fact. 2392109382Syar */ 2393109382Syar if (atstart && isdigit(c)) 2394109382Syar (void) putc(' ', stdout); 23951592Srgrimes (void) putc(c, stdout); 2396109382Syar atstart = (c == '\n'); 23971592Srgrimes } 23981592Srgrimes (void) ftpd_pclose(fin); 2399137852Syar reply(code, "End of status."); 24001592Srgrimes} 24011592Srgrimes 24021592Srgrimesvoid 240390148Simpstatcmd(void) 24041592Srgrimes{ 240556668Sshin union sockunion *su; 24061592Srgrimes u_char *a, *p; 240799255Sume char hname[NI_MAXHOST]; 240856668Sshin int ispassive; 24091592Srgrimes 2410110037Syar if (hostinfo) { 2411110037Syar lreply(211, "%s FTP server status:", hostname); 2412110037Syar printf(" %s\r\n", version); 2413110037Syar } else 2414110037Syar lreply(211, "FTP server status:"); 24151592Srgrimes printf(" Connected to %s", remotehost); 241656668Sshin if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 241799255Sume hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 2418137983Syar hname[sizeof(hname) - 1] = 0; 241956668Sshin if (strcmp(hname, remotehost) != 0) 242056668Sshin printf(" (%s)", hname); 242156668Sshin } 24221592Srgrimes printf("\r\n"); 24231592Srgrimes if (logged_in) { 24241592Srgrimes if (guest) 24251592Srgrimes printf(" Logged in anonymously\r\n"); 24261592Srgrimes else 24271592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 24281592Srgrimes } else if (askpasswd) 24291592Srgrimes printf(" Waiting for password\r\n"); 24301592Srgrimes else 24311592Srgrimes printf(" Waiting for user name\r\n"); 24321592Srgrimes printf(" TYPE: %s", typenames[type]); 24331592Srgrimes if (type == TYPE_A || type == TYPE_E) 24341592Srgrimes printf(", FORM: %s", formnames[form]); 24351592Srgrimes if (type == TYPE_L) 2436103949Smike#if CHAR_BIT == 8 2437103949Smike printf(" %d", CHAR_BIT); 24381592Srgrimes#else 24391592Srgrimes printf(" %d", bytesize); /* need definition! */ 24401592Srgrimes#endif 24411592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 24421592Srgrimes strunames[stru], modenames[mode]); 24431592Srgrimes if (data != -1) 24441592Srgrimes printf(" Data connection open\r\n"); 24451592Srgrimes else if (pdata != -1) { 244656668Sshin ispassive = 1; 244756668Sshin su = &pasv_addr; 24481592Srgrimes goto printaddr; 24491592Srgrimes } else if (usedefault == 0) { 245056668Sshin ispassive = 0; 245156668Sshin su = &data_dest; 24521592Srgrimesprintaddr: 24531592Srgrimes#define UC(b) (((int) b) & 0xff) 245456668Sshin if (epsvall) { 245556668Sshin printf(" EPSV only mode (EPSV ALL)\r\n"); 245656668Sshin goto epsvonly; 245756668Sshin } 245856668Sshin 245956668Sshin /* PORT/PASV */ 246056668Sshin if (su->su_family == AF_INET) { 246156668Sshin a = (u_char *) &su->su_sin.sin_addr; 246256668Sshin p = (u_char *) &su->su_sin.sin_port; 246356668Sshin printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 246456668Sshin ispassive ? "PASV" : "PORT", 246556668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 246656668Sshin UC(p[0]), UC(p[1])); 246756668Sshin } 246856668Sshin 246956668Sshin /* LPRT/LPSV */ 247056668Sshin { 247156668Sshin int alen, af, i; 247256668Sshin 247356668Sshin switch (su->su_family) { 247456668Sshin case AF_INET: 247556668Sshin a = (u_char *) &su->su_sin.sin_addr; 247656668Sshin p = (u_char *) &su->su_sin.sin_port; 247756668Sshin alen = sizeof(su->su_sin.sin_addr); 247856668Sshin af = 4; 247956668Sshin break; 248056668Sshin case AF_INET6: 248156668Sshin a = (u_char *) &su->su_sin6.sin6_addr; 248256668Sshin p = (u_char *) &su->su_sin6.sin6_port; 248356668Sshin alen = sizeof(su->su_sin6.sin6_addr); 248456668Sshin af = 6; 248556668Sshin break; 248656668Sshin default: 248756668Sshin af = 0; 248856668Sshin break; 248956668Sshin } 249056668Sshin if (af) { 249156668Sshin printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 249256668Sshin af, alen); 249356668Sshin for (i = 0; i < alen; i++) 249456668Sshin printf("%d,", UC(a[i])); 249556668Sshin printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 249656668Sshin } 249756668Sshin } 249856668Sshin 249956668Sshinepsvonly:; 250056668Sshin /* EPRT/EPSV */ 250156668Sshin { 250256668Sshin int af; 250356668Sshin 250456668Sshin switch (su->su_family) { 250556668Sshin case AF_INET: 250656668Sshin af = 1; 250756668Sshin break; 250856668Sshin case AF_INET6: 250956668Sshin af = 2; 251056668Sshin break; 251156668Sshin default: 251256668Sshin af = 0; 251356668Sshin break; 251456668Sshin } 251556668Sshin if (af) { 251699255Sume union sockunion tmp; 251799255Sume 251899255Sume tmp = *su; 251999255Sume if (tmp.su_family == AF_INET6) 252099255Sume tmp.su_sin6.sin6_scope_id = 0; 252199255Sume if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 252256668Sshin hname, sizeof(hname) - 1, NULL, 0, 252356668Sshin NI_NUMERICHOST)) { 2524137983Syar hname[sizeof(hname) - 1] = 0; 252556668Sshin printf(" %s |%d|%s|%d|\r\n", 252656668Sshin ispassive ? "EPSV" : "EPRT", 252799255Sume af, hname, htons(tmp.su_port)); 252856668Sshin } 252956668Sshin } 253056668Sshin } 25311592Srgrimes#undef UC 25321592Srgrimes } else 25331592Srgrimes printf(" No data connection\r\n"); 2534137852Syar reply(211, "End of status."); 25351592Srgrimes} 25361592Srgrimes 25371592Srgrimesvoid 253890148Simpfatalerror(char *s) 25391592Srgrimes{ 25401592Srgrimes 2541137849Syar reply(451, "Error in server: %s", s); 25421592Srgrimes reply(221, "Closing connection due to server error."); 25431592Srgrimes dologout(0); 25441592Srgrimes /* NOTREACHED */ 25451592Srgrimes} 25461592Srgrimes 25471592Srgrimesvoid 25481592Srgrimesreply(int n, const char *fmt, ...) 25491592Srgrimes{ 25501592Srgrimes va_list ap; 255190148Simp 2552129170Stjr (void)printf("%d ", n); 25531592Srgrimes va_start(ap, fmt); 25541592Srgrimes (void)vprintf(fmt, ap); 2555129170Stjr va_end(ap); 25561592Srgrimes (void)printf("\r\n"); 25571592Srgrimes (void)fflush(stdout); 255876096Smarkm if (ftpdebug) { 25591592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 2560129170Stjr va_start(ap, fmt); 25611592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2562129170Stjr va_end(ap); 25631592Srgrimes } 25641592Srgrimes} 25651592Srgrimes 25661592Srgrimesvoid 25671592Srgrimeslreply(int n, const char *fmt, ...) 25681592Srgrimes{ 25691592Srgrimes va_list ap; 257090148Simp 2571129170Stjr (void)printf("%d- ", n); 25721592Srgrimes va_start(ap, fmt); 25731592Srgrimes (void)vprintf(fmt, ap); 2574129170Stjr va_end(ap); 25751592Srgrimes (void)printf("\r\n"); 25761592Srgrimes (void)fflush(stdout); 257776096Smarkm if (ftpdebug) { 25781592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 2579129170Stjr va_start(ap, fmt); 25801592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2581129170Stjr va_end(ap); 25821592Srgrimes } 25831592Srgrimes} 25841592Srgrimes 25851592Srgrimesstatic void 258690148Simpack(char *s) 25871592Srgrimes{ 25881592Srgrimes 25891592Srgrimes reply(250, "%s command successful.", s); 25901592Srgrimes} 25911592Srgrimes 25921592Srgrimesvoid 259390148Simpnack(char *s) 25941592Srgrimes{ 25951592Srgrimes 25961592Srgrimes reply(502, "%s command not implemented.", s); 25971592Srgrimes} 25981592Srgrimes 25991592Srgrimes/* ARGSUSED */ 26001592Srgrimesvoid 260190148Simpyyerror(char *s) 26021592Srgrimes{ 26031592Srgrimes char *cp; 26041592Srgrimes 260517478Smarkm if ((cp = strchr(cbuf,'\n'))) 26061592Srgrimes *cp = '\0'; 2607137852Syar reply(500, "%s: command not understood.", cbuf); 26081592Srgrimes} 26091592Srgrimes 26101592Srgrimesvoid 261190148Simpdelete(char *name) 26121592Srgrimes{ 26131592Srgrimes struct stat st; 26141592Srgrimes 26151592Srgrimes LOGCMD("delete", name); 2616100439Syar if (lstat(name, &st) < 0) { 26171592Srgrimes perror_reply(550, name); 26181592Srgrimes return; 26191592Srgrimes } 2620137847Syar if (S_ISDIR(st.st_mode)) { 26211592Srgrimes if (rmdir(name) < 0) { 26221592Srgrimes perror_reply(550, name); 26231592Srgrimes return; 26241592Srgrimes } 26251592Srgrimes goto done; 26261592Srgrimes } 2627125568Syar if (guest && noguestmod) { 2628137852Syar reply(550, "Operation not permitted."); 2629125568Syar return; 2630125568Syar } 2631125568Syar if (unlink(name) < 0) { 26321592Srgrimes perror_reply(550, name); 26331592Srgrimes return; 26341592Srgrimes } 26351592Srgrimesdone: 26361592Srgrimes ack("DELE"); 26371592Srgrimes} 26381592Srgrimes 26391592Srgrimesvoid 264090148Simpcwd(char *path) 26411592Srgrimes{ 26421592Srgrimes 26431592Srgrimes if (chdir(path) < 0) 26441592Srgrimes perror_reply(550, path); 26451592Srgrimes else 26461592Srgrimes ack("CWD"); 26471592Srgrimes} 26481592Srgrimes 26491592Srgrimesvoid 265090148Simpmakedir(char *name) 26511592Srgrimes{ 2652100878Syar char *s; 26531592Srgrimes 26541592Srgrimes LOGCMD("mkdir", name); 265599195Smdodd if (guest && noguestmkd) 2656137853Syar reply(550, "Operation not permitted."); 265799195Smdodd else if (mkdir(name, 0777) < 0) 26581592Srgrimes perror_reply(550, name); 2659100878Syar else { 2660100878Syar if ((s = doublequote(name)) == NULL) 2661100878Syar fatalerror("Ran out of memory."); 2662100878Syar reply(257, "\"%s\" directory created.", s); 2663100878Syar free(s); 2664100878Syar } 26651592Srgrimes} 26661592Srgrimes 26671592Srgrimesvoid 266890148Simpremovedir(char *name) 26691592Srgrimes{ 26701592Srgrimes 26711592Srgrimes LOGCMD("rmdir", name); 26721592Srgrimes if (rmdir(name) < 0) 26731592Srgrimes perror_reply(550, name); 26741592Srgrimes else 26751592Srgrimes ack("RMD"); 26761592Srgrimes} 26771592Srgrimes 26781592Srgrimesvoid 267990148Simppwd(void) 26801592Srgrimes{ 2681100486Syar char *s, path[MAXPATHLEN + 1]; 26821592Srgrimes 2683137830Syar if (getcwd(path, sizeof(path)) == NULL) 2684137839Syar perror_reply(550, "Get current directory"); 2685100486Syar else { 2686100486Syar if ((s = doublequote(path)) == NULL) 2687100486Syar fatalerror("Ran out of memory."); 2688100486Syar reply(257, "\"%s\" is current directory.", s); 2689100486Syar free(s); 2690100486Syar } 26911592Srgrimes} 26921592Srgrimes 26931592Srgrimeschar * 269490148Simprenamefrom(char *name) 26951592Srgrimes{ 26961592Srgrimes struct stat st; 26971592Srgrimes 2698125569Syar if (guest && noguestmod) { 2699137852Syar reply(550, "Operation not permitted."); 2700125569Syar return (NULL); 2701125569Syar } 2702100439Syar if (lstat(name, &st) < 0) { 27031592Srgrimes perror_reply(550, name); 2704125570Syar return (NULL); 27051592Srgrimes } 2706137852Syar reply(350, "File exists, ready for destination name."); 27071592Srgrimes return (name); 27081592Srgrimes} 27091592Srgrimes 27101592Srgrimesvoid 271190148Simprenamecmd(char *from, char *to) 27121592Srgrimes{ 271317433Spst struct stat st; 27141592Srgrimes 27151592Srgrimes LOGCMD2("rename", from, to); 271617433Spst 271717433Spst if (guest && (stat(to, &st) == 0)) { 2718137852Syar reply(550, "%s: permission denied.", to); 271917433Spst return; 272017433Spst } 272117433Spst 27221592Srgrimes if (rename(from, to) < 0) 27231592Srgrimes perror_reply(550, "rename"); 27241592Srgrimes else 27251592Srgrimes ack("RNTO"); 27261592Srgrimes} 27271592Srgrimes 27281592Srgrimesstatic void 272990148Simpdolog(struct sockaddr *who) 27301592Srgrimes{ 2731137984Syar char who_name[NI_MAXHOST]; 27321592Srgrimes 273356668Sshin realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 2734137983Syar remotehost[sizeof(remotehost) - 1] = 0; 2735137984Syar if (getnameinfo(who, who->sa_len, 2736137984Syar who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) 2737137984Syar *who_name = 0; 2738137984Syar who_name[sizeof(who_name) - 1] = 0; 273956668Sshin 27401592Srgrimes#ifdef SETPROCTITLE 274125283Sdavidn#ifdef VIRTUAL_HOSTING 274225283Sdavidn if (thishost != firsthost) 274325283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 274425283Sdavidn remotehost, hostname); 274525283Sdavidn else 274625283Sdavidn#endif 274725283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected", 274825283Sdavidn remotehost); 274913139Speter setproctitle("%s", proctitle); 27501592Srgrimes#endif /* SETPROCTITLE */ 27511592Srgrimes 275225283Sdavidn if (logging) { 275325283Sdavidn#ifdef VIRTUAL_HOSTING 275425283Sdavidn if (thishost != firsthost) 2755137984Syar syslog(LOG_INFO, "connection from %s (%s) to %s", 2756137984Syar remotehost, who_name, hostname); 275725283Sdavidn else 275825283Sdavidn#endif 2759137984Syar syslog(LOG_INFO, "connection from %s (%s)", 2760137984Syar remotehost, who_name); 276125283Sdavidn } 27621592Srgrimes} 27631592Srgrimes 27641592Srgrimes/* 27651592Srgrimes * Record logout in wtmp file 27661592Srgrimes * and exit with supplied status. 27671592Srgrimes */ 27681592Srgrimesvoid 276990148Simpdologout(int status) 27701592Srgrimes{ 27711592Srgrimes 2772202604Sed if (logged_in && dowtmp) { 2773132893Syar (void) seteuid(0); 2774202604Sed ftpd_logwtmp(wtmpid, NULL, NULL); 27751592Srgrimes } 27761592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 27771592Srgrimes _exit(status); 27781592Srgrimes} 27791592Srgrimes 27801592Srgrimesstatic void 278190148Simpsigurg(int signo) 27821592Srgrimes{ 278389935Syar 278489935Syar recvurg = 1; 278589935Syar} 278689935Syar 278789935Syarstatic void 2788140472Syarmaskurg(int flag) 2789140472Syar{ 2790140472Syar int oerrno; 2791140472Syar sigset_t sset; 2792140472Syar 2793140472Syar if (!transflag) { 2794140472Syar syslog(LOG_ERR, "Internal: maskurg() while no transfer"); 2795140472Syar return; 2796140472Syar } 2797140472Syar oerrno = errno; 2798140472Syar sigemptyset(&sset); 2799140472Syar sigaddset(&sset, SIGURG); 2800140472Syar sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); 2801140472Syar errno = oerrno; 2802140472Syar} 2803140472Syar 2804140472Syarstatic void 2805140472Syarflagxfer(int flag) 2806140472Syar{ 2807140472Syar 2808140472Syar if (flag) { 2809141967Syar if (transflag) 2810141967Syar syslog(LOG_ERR, "Internal: flagxfer(1): " 2811141967Syar "transfer already under way"); 2812141966Syar transflag = 1; 2813141966Syar maskurg(0); 2814140472Syar recvurg = 0; 2815141966Syar } else { 2816141967Syar if (!transflag) 2817141967Syar syslog(LOG_ERR, "Internal: flagxfer(0): " 2818141967Syar "no active transfer"); 2819141966Syar maskurg(1); 2820140472Syar transflag = 0; 2821141966Syar } 2822140472Syar} 2823140472Syar 2824140472Syar/* 2825140472Syar * Returns 0 if OK to resume or -1 if abort requested. 2826140472Syar */ 2827140472Syarstatic int 282890148Simpmyoob(void) 282989935Syar{ 28301592Srgrimes char *cp; 2831186405Scperciva int ret; 28321592Srgrimes 2833140472Syar if (!transflag) { 2834140472Syar syslog(LOG_ERR, "Internal: myoob() while no transfer"); 2835140472Syar return (0); 2836140472Syar } 28371592Srgrimes cp = tmpline; 2838299356Sbapt ret = get_line(cp, 7, stdin); 2839186405Scperciva if (ret == -1) { 28401592Srgrimes reply(221, "You could at least say goodbye."); 28411592Srgrimes dologout(0); 2842186405Scperciva } else if (ret == -2) { 2843186405Scperciva /* Ignore truncated command. */ 2844186405Scperciva return (0); 28451592Srgrimes } 28461592Srgrimes upper(cp); 28471592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 28481592Srgrimes tmpline[0] = '\0'; 28491592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 2850137852Syar reply(226, "Abort successful."); 2851140472Syar return (-1); 28521592Srgrimes } 28531592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 285451192Smharo tmpline[0] = '\0'; 2855132930Syar if (file_size != -1) 2856137852Syar reply(213, "Status: %jd of %jd bytes transferred.", 2857132929Syar (intmax_t)byte_count, (intmax_t)file_size); 28581592Srgrimes else 2859137852Syar reply(213, "Status: %jd bytes transferred.", 2860132929Syar (intmax_t)byte_count); 28611592Srgrimes } 2862140472Syar return (0); 28631592Srgrimes} 28641592Srgrimes 28651592Srgrimes/* 28661592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 28671592Srgrimes * the PASV command in RFC959. However, it has been blessed as 28681592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 28691592Srgrimes * with Rick Adams on 25 Jan 89. 28701592Srgrimes */ 28711592Srgrimesvoid 287290148Simppassive(void) 28731592Srgrimes{ 2874141918Sstefanf socklen_t len; 2875141918Sstefanf int on; 28761592Srgrimes char *p, *a; 28771592Srgrimes 287817433Spst if (pdata >= 0) /* close old port if one set */ 287917433Spst close(pdata); 288017433Spst 288156668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 28821592Srgrimes if (pdata < 0) { 28831592Srgrimes perror_reply(425, "Can't open passive connection"); 28841592Srgrimes return; 28851592Srgrimes } 2886100615Syar on = 1; 2887100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2888100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 28899933Spst 2890132893Syar (void) seteuid(0); 289117433Spst 289219903Spst#ifdef IP_PORTRANGE 289356668Sshin if (ctrl_addr.su_family == AF_INET) { 2894100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2895100615Syar : IP_PORTRANGE_DEFAULT; 289619903Spst 289719903Spst if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2898100612Syar &on, sizeof(on)) < 0) 289919903Spst goto pasv_error; 29001592Srgrimes } 290119903Spst#endif 290260929Snsayer#ifdef IPV6_PORTRANGE 290360929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2904100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2905100615Syar : IPV6_PORTRANGE_DEFAULT; 29069933Spst 290760929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2908100612Syar &on, sizeof(on)) < 0) 290960929Snsayer goto pasv_error; 291060929Snsayer } 291160929Snsayer#endif 291260929Snsayer 291316033Speter pasv_addr = ctrl_addr; 291456668Sshin pasv_addr.su_port = 0; 291556668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 291616033Speter goto pasv_error; 291717433Spst 2918132893Syar (void) seteuid(pw->pw_uid); 291916033Speter 29201592Srgrimes len = sizeof(pasv_addr); 29211592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 29221592Srgrimes goto pasv_error; 29231592Srgrimes if (listen(pdata, 1) < 0) 29241592Srgrimes goto pasv_error; 292556668Sshin if (pasv_addr.su_family == AF_INET) 292656668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 292756668Sshin else if (pasv_addr.su_family == AF_INET6 && 292856668Sshin IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 292956668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 293056668Sshin else 293156668Sshin goto pasv_error; 293256668Sshin 293356668Sshin p = (char *) &pasv_addr.su_port; 29341592Srgrimes 29351592Srgrimes#define UC(b) (((int) b) & 0xff) 29361592Srgrimes 29371592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 29381592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 29391592Srgrimes return; 29401592Srgrimes 29411592Srgrimespasv_error: 2942132893Syar (void) seteuid(pw->pw_uid); 29431592Srgrimes (void) close(pdata); 29441592Srgrimes pdata = -1; 29451592Srgrimes perror_reply(425, "Can't open passive connection"); 29461592Srgrimes return; 29471592Srgrimes} 29481592Srgrimes 29491592Srgrimes/* 295056668Sshin * Long Passive defined in RFC 1639. 295156668Sshin * 228 Entering Long Passive Mode 295256668Sshin * (af, hal, h1, h2, h3,..., pal, p1, p2...) 295356668Sshin */ 295456668Sshin 295556668Sshinvoid 295690148Simplong_passive(char *cmd, int pf) 295756668Sshin{ 2958141918Sstefanf socklen_t len; 2959141918Sstefanf int on; 296056668Sshin char *p, *a; 296156668Sshin 296256668Sshin if (pdata >= 0) /* close old port if one set */ 296356668Sshin close(pdata); 296456668Sshin 296556668Sshin if (pf != PF_UNSPEC) { 296656668Sshin if (ctrl_addr.su_family != pf) { 296756668Sshin switch (ctrl_addr.su_family) { 296856668Sshin case AF_INET: 296956668Sshin pf = 1; 297056668Sshin break; 297156668Sshin case AF_INET6: 297256668Sshin pf = 2; 297356668Sshin break; 297456668Sshin default: 297556668Sshin pf = 0; 297656668Sshin break; 297756668Sshin } 297856668Sshin /* 297956668Sshin * XXX 298056668Sshin * only EPRT/EPSV ready clients will understand this 298156668Sshin */ 298256668Sshin if (strcmp(cmd, "EPSV") == 0 && pf) { 298356668Sshin reply(522, "Network protocol mismatch, " 298456668Sshin "use (%d)", pf); 298556668Sshin } else 2986137852Syar reply(501, "Network protocol mismatch."); /*XXX*/ 298756668Sshin 298856668Sshin return; 298956668Sshin } 299056668Sshin } 299156668Sshin 299256668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 299356668Sshin if (pdata < 0) { 299456668Sshin perror_reply(425, "Can't open passive connection"); 299556668Sshin return; 299656668Sshin } 2997100615Syar on = 1; 2998100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2999100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 300056668Sshin 3001132893Syar (void) seteuid(0); 300256668Sshin 300356668Sshin pasv_addr = ctrl_addr; 300456668Sshin pasv_addr.su_port = 0; 300556668Sshin len = pasv_addr.su_len; 300656668Sshin 300760929Snsayer#ifdef IP_PORTRANGE 300860929Snsayer if (ctrl_addr.su_family == AF_INET) { 3009100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 3010100615Syar : IP_PORTRANGE_DEFAULT; 301160929Snsayer 301260929Snsayer if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 3013100612Syar &on, sizeof(on)) < 0) 301460929Snsayer goto pasv_error; 301560929Snsayer } 301660929Snsayer#endif 301760929Snsayer#ifdef IPV6_PORTRANGE 301860929Snsayer if (ctrl_addr.su_family == AF_INET6) { 3019100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 3020100615Syar : IPV6_PORTRANGE_DEFAULT; 302160929Snsayer 302260929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 3023100612Syar &on, sizeof(on)) < 0) 302460929Snsayer goto pasv_error; 302560929Snsayer } 302660929Snsayer#endif 302760929Snsayer 302856668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 302956668Sshin goto pasv_error; 303056668Sshin 3031132893Syar (void) seteuid(pw->pw_uid); 303256668Sshin 303356668Sshin if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 303456668Sshin goto pasv_error; 303556668Sshin if (listen(pdata, 1) < 0) 303656668Sshin goto pasv_error; 303756668Sshin 303856668Sshin#define UC(b) (((int) b) & 0xff) 303956668Sshin 304056668Sshin if (strcmp(cmd, "LPSV") == 0) { 304156668Sshin p = (char *)&pasv_addr.su_port; 304256668Sshin switch (pasv_addr.su_family) { 304356668Sshin case AF_INET: 304456668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 304556668Sshin v4_reply: 304656668Sshin reply(228, 304756668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 304856668Sshin 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 304956668Sshin 2, UC(p[0]), UC(p[1])); 305056668Sshin return; 305156668Sshin case AF_INET6: 305256668Sshin if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 305356668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 305456668Sshin goto v4_reply; 305556668Sshin } 305656668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr; 305756668Sshin reply(228, 305856668Sshin"Entering Long Passive Mode " 305956668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 306056668Sshin 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 306156668Sshin UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 306256668Sshin UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 306356668Sshin UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 306456668Sshin 2, UC(p[0]), UC(p[1])); 306556668Sshin return; 306656668Sshin } 306756668Sshin } else if (strcmp(cmd, "EPSV") == 0) { 306856668Sshin switch (pasv_addr.su_family) { 306956668Sshin case AF_INET: 307056668Sshin case AF_INET6: 307156668Sshin reply(229, "Entering Extended Passive Mode (|||%d|)", 307256668Sshin ntohs(pasv_addr.su_port)); 307356668Sshin return; 307456668Sshin } 307556668Sshin } else { 307656668Sshin /* more proper error code? */ 307756668Sshin } 307856668Sshin 307956668Sshinpasv_error: 3080132893Syar (void) seteuid(pw->pw_uid); 308156668Sshin (void) close(pdata); 308256668Sshin pdata = -1; 308356668Sshin perror_reply(425, "Can't open passive connection"); 308456668Sshin return; 308556668Sshin} 308656668Sshin 308756668Sshin/* 3088101537Syar * Generate unique name for file with basename "local" 3089101537Syar * and open the file in order to avoid possible races. 3090101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 3091101537Syar * Return descriptor to the file, set "name" to its name. 3092101537Syar * 30931592Srgrimes * Generates failure reply on error. 30941592Srgrimes */ 3095101537Syarstatic int 3096101537Syarguniquefd(char *local, char **name) 30971592Srgrimes{ 30981592Srgrimes static char new[MAXPATHLEN]; 30991592Srgrimes struct stat st; 3100101537Syar char *cp; 31011592Srgrimes int count; 3102101537Syar int fd; 31031592Srgrimes 31041592Srgrimes cp = strrchr(local, '/'); 31051592Srgrimes if (cp) 31061592Srgrimes *cp = '\0'; 31071592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 31081592Srgrimes perror_reply(553, cp ? local : "."); 3109101537Syar return (-1); 31101592Srgrimes } 3111101537Syar if (cp) { 3112101537Syar /* 3113101537Syar * Let not overwrite dirname with counter suffix. 3114101537Syar * -4 is for /nn\0 3115101537Syar * In this extreme case dot won't be put in front of suffix. 3116101537Syar */ 3117101537Syar if (strlen(local) > sizeof(new) - 4) { 3118137852Syar reply(553, "Pathname too long."); 3119101537Syar return (-1); 3120101537Syar } 31211592Srgrimes *cp = '/'; 3122101537Syar } 312331973Simp /* -4 is for the .nn<null> we put on the end below */ 312431973Simp (void) snprintf(new, sizeof(new) - 4, "%s", local); 31251592Srgrimes cp = new + strlen(new); 3126101537Syar /* 3127101537Syar * Don't generate dotfile unless requested explicitly. 3128101537Syar * This covers the case when basename gets truncated off 3129101537Syar * by buffer size. 3130101537Syar */ 3131101537Syar if (cp > new && cp[-1] != '/') 3132101537Syar *cp++ = '.'; 3133101537Syar for (count = 0; count < 100; count++) { 3134101537Syar /* At count 0 try unmodified name */ 3135101537Syar if (count) 3136101537Syar (void)sprintf(cp, "%d", count); 3137101537Syar if ((fd = open(count ? new : local, 3138101537Syar O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 3139101537Syar *name = count ? new : local; 3140101537Syar return (fd); 3141101537Syar } 3142110046Syar if (errno != EEXIST) { 3143110307Syar perror_reply(553, count ? new : local); 3144110046Syar return (-1); 3145110046Syar } 31461592Srgrimes } 31471592Srgrimes reply(452, "Unique file name cannot be created."); 3148101537Syar return (-1); 31491592Srgrimes} 31501592Srgrimes 31511592Srgrimes/* 31521592Srgrimes * Format and send reply containing system error number. 31531592Srgrimes */ 31541592Srgrimesvoid 315590148Simpperror_reply(int code, char *string) 31561592Srgrimes{ 31571592Srgrimes 31581592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 31591592Srgrimes} 31601592Srgrimes 31611592Srgrimesstatic char *onefile[] = { 31621592Srgrimes "", 31631592Srgrimes 0 31641592Srgrimes}; 31651592Srgrimes 31661592Srgrimesvoid 316790148Simpsend_file_list(char *whichf) 31681592Srgrimes{ 31691592Srgrimes struct stat st; 31701592Srgrimes DIR *dirp = NULL; 31711592Srgrimes struct dirent *dir; 31721592Srgrimes FILE *dout = NULL; 31731592Srgrimes char **dirlist, *dirname; 31741592Srgrimes int simple = 0; 31751592Srgrimes int freeglob = 0; 31761592Srgrimes glob_t gl; 31771592Srgrimes 31781592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 3179100222Smikeh int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 31801592Srgrimes 31811592Srgrimes memset(&gl, 0, sizeof(gl)); 318274470Sjlemon gl.gl_matchc = MAXGLOBARGS; 318380525Smikeh flags |= GLOB_LIMIT; 31841592Srgrimes freeglob = 1; 31851592Srgrimes if (glob(whichf, flags, 0, &gl)) { 3186137852Syar reply(550, "No matching files found."); 31871592Srgrimes goto out; 31881592Srgrimes } else if (gl.gl_pathc == 0) { 31891592Srgrimes errno = ENOENT; 31901592Srgrimes perror_reply(550, whichf); 31911592Srgrimes goto out; 31921592Srgrimes } 31931592Srgrimes dirlist = gl.gl_pathv; 31941592Srgrimes } else { 31951592Srgrimes onefile[0] = whichf; 31961592Srgrimes dirlist = onefile; 31971592Srgrimes simple = 1; 31981592Srgrimes } 31991592Srgrimes 320017478Smarkm while ((dirname = *dirlist++)) { 32011592Srgrimes if (stat(dirname, &st) < 0) { 32021592Srgrimes /* 32031592Srgrimes * If user typed "ls -l", etc, and the client 32041592Srgrimes * used NLST, do what the user meant. 32051592Srgrimes */ 32061592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 3207140472Syar dout == NULL) 320825165Sdavidn retrieve(_PATH_LS " %s", dirname); 3209140472Syar else 3210140472Syar perror_reply(550, whichf); 32111592Srgrimes goto out; 32121592Srgrimes } 32131592Srgrimes 32141592Srgrimes if (S_ISREG(st.st_mode)) { 32151592Srgrimes if (dout == NULL) { 3216132930Syar dout = dataconn("file list", -1, "w"); 32171592Srgrimes if (dout == NULL) 32181592Srgrimes goto out; 3219140472Syar STARTXFER; 32201592Srgrimes } 3221140472Syar START_UNSAFE; 32221592Srgrimes fprintf(dout, "%s%s\n", dirname, 32231592Srgrimes type == TYPE_A ? "\r" : ""); 3224140472Syar END_UNSAFE; 3225140472Syar if (ferror(dout)) 3226140472Syar goto data_err; 3227140472Syar byte_count += strlen(dirname) + 3228140472Syar (type == TYPE_A ? 2 : 1); 3229140472Syar CHECKOOB(goto abrt); 32301592Srgrimes continue; 32311592Srgrimes } else if (!S_ISDIR(st.st_mode)) 32321592Srgrimes continue; 32331592Srgrimes 32341592Srgrimes if ((dirp = opendir(dirname)) == NULL) 32351592Srgrimes continue; 32361592Srgrimes 32371592Srgrimes while ((dir = readdir(dirp)) != NULL) { 32381592Srgrimes char nbuf[MAXPATHLEN]; 32391592Srgrimes 3240140472Syar CHECKOOB(goto abrt); 324189935Syar 32421592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 32431592Srgrimes continue; 32441592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 32451592Srgrimes dir->d_namlen == 2) 32461592Srgrimes continue; 32471592Srgrimes 324899213Smaxim snprintf(nbuf, sizeof(nbuf), 324931973Simp "%s/%s", dirname, dir->d_name); 32501592Srgrimes 32511592Srgrimes /* 32521592Srgrimes * We have to do a stat to insure it's 32531592Srgrimes * not a directory or special file. 32541592Srgrimes */ 32551592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 32561592Srgrimes S_ISREG(st.st_mode))) { 32571592Srgrimes if (dout == NULL) { 3258132930Syar dout = dataconn("file list", -1, "w"); 32591592Srgrimes if (dout == NULL) 32601592Srgrimes goto out; 3261140472Syar STARTXFER; 32621592Srgrimes } 3263140472Syar START_UNSAFE; 32641592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 32651592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 32661592Srgrimes type == TYPE_A ? "\r" : ""); 32671592Srgrimes else 32681592Srgrimes fprintf(dout, "%s%s\n", nbuf, 32691592Srgrimes type == TYPE_A ? "\r" : ""); 3270140472Syar END_UNSAFE; 3271140472Syar if (ferror(dout)) 3272140472Syar goto data_err; 3273140472Syar byte_count += strlen(nbuf) + 3274140472Syar (type == TYPE_A ? 2 : 1); 3275140472Syar CHECKOOB(goto abrt); 32761592Srgrimes } 32771592Srgrimes } 32781592Srgrimes (void) closedir(dirp); 3279140472Syar dirp = NULL; 32801592Srgrimes } 32811592Srgrimes 32821592Srgrimes if (dout == NULL) 32831592Srgrimes reply(550, "No files found."); 3284140472Syar else if (ferror(dout)) 3285140472Syardata_err: perror_reply(550, "Data connection"); 32861592Srgrimes else 32871592Srgrimes reply(226, "Transfer complete."); 3288140472Syarout: 3289140472Syar if (dout) { 3290140472Syar ENDXFER; 3291140472Syarabrt: 32921592Srgrimes (void) fclose(dout); 3293140472Syar data = -1; 3294140472Syar pdata = -1; 3295140472Syar } 3296140472Syar if (dirp) 3297140472Syar (void) closedir(dirp); 32981592Srgrimes if (freeglob) { 32991592Srgrimes freeglob = 0; 33001592Srgrimes globfree(&gl); 33011592Srgrimes } 33021592Srgrimes} 33031592Srgrimes 330415196Sdgvoid 330590148Simpreapchild(int signo) 330615196Sdg{ 3307137830Syar while (waitpid(-1, NULL, WNOHANG) > 0); 330815196Sdg} 330915196Sdg 331013139Speter#ifdef OLD_SETPROCTITLE 33111592Srgrimes/* 33121592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 33131592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 33141592Srgrimes * have much of an environment or arglist to overwrite. 33151592Srgrimes */ 33161592Srgrimesvoid 33171592Srgrimessetproctitle(const char *fmt, ...) 33181592Srgrimes{ 33191592Srgrimes int i; 33201592Srgrimes va_list ap; 33211592Srgrimes char *p, *bp, ch; 33221592Srgrimes char buf[LINE_MAX]; 33231592Srgrimes 33241592Srgrimes va_start(ap, fmt); 33251592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 33261592Srgrimes 33271592Srgrimes /* make ps print our process name */ 33281592Srgrimes p = Argv[0]; 33291592Srgrimes *p++ = '-'; 33301592Srgrimes 33311592Srgrimes i = strlen(buf); 33321592Srgrimes if (i > LastArgv - p - 2) { 33331592Srgrimes i = LastArgv - p - 2; 33341592Srgrimes buf[i] = '\0'; 33351592Srgrimes } 33361592Srgrimes bp = buf; 33371592Srgrimes while (ch = *bp++) 33381592Srgrimes if (ch != '\n' && ch != '\r') 33391592Srgrimes *p++ = ch; 33401592Srgrimes while (p < LastArgv) 33411592Srgrimes *p++ = ' '; 33421592Srgrimes} 334313139Speter#endif /* OLD_SETPROCTITLE */ 33446740Sguido 334517433Spststatic void 3346137848Syarappendf(char **strp, char *fmt, ...) 3347137848Syar{ 3348137848Syar va_list ap; 3349137848Syar char *ostr, *p; 3350137848Syar 3351137848Syar va_start(ap, fmt); 3352137848Syar vasprintf(&p, fmt, ap); 3353137848Syar va_end(ap); 3354137848Syar if (p == NULL) 3355137848Syar fatalerror("Ran out of memory."); 3356137848Syar if (*strp == NULL) 3357137848Syar *strp = p; 3358137848Syar else { 3359137848Syar ostr = *strp; 3360137848Syar asprintf(strp, "%s%s", ostr, p); 3361137848Syar if (*strp == NULL) 3362137848Syar fatalerror("Ran out of memory."); 3363137848Syar free(ostr); 3364137848Syar } 3365137848Syar} 3366137848Syar 3367137848Syarstatic void 3368137848Syarlogcmd(char *cmd, char *file1, char *file2, off_t cnt) 3369137848Syar{ 3370137848Syar char *msg = NULL; 3371137848Syar char wd[MAXPATHLEN + 1]; 3372137848Syar 3373137848Syar if (logging <= 1) 3374137848Syar return; 3375137848Syar 3376137985Syar if (getcwd(wd, sizeof(wd) - 1) == NULL) 3377137985Syar strcpy(wd, strerror(errno)); 3378137985Syar 3379137848Syar appendf(&msg, "%s", cmd); 3380137848Syar if (file1) 3381137848Syar appendf(&msg, " %s", file1); 3382137848Syar if (file2) 3383137848Syar appendf(&msg, " %s", file2); 3384137848Syar if (cnt >= 0) 3385137848Syar appendf(&msg, " = %jd bytes", (intmax_t)cnt); 3386137985Syar appendf(&msg, " (wd: %s", wd); 3387137862Syar if (guest || dochroot) 3388137985Syar appendf(&msg, "; chrooted"); 3389137985Syar appendf(&msg, ")"); 3390137848Syar syslog(LOG_INFO, "%s", msg); 3391137848Syar free(msg); 3392137848Syar} 3393137848Syar 3394137848Syarstatic void 339590148Simplogxfer(char *name, off_t size, time_t start) 33966740Sguido{ 3397137145Syar char buf[MAXPATHLEN + 1024]; 33986740Sguido char path[MAXPATHLEN + 1]; 339936612Sjb time_t now; 34006740Sguido 3401137145Syar if (statfd >= 0) { 34026740Sguido time(&now); 3403137145Syar if (realpath(name, path) == NULL) { 3404137145Syar syslog(LOG_NOTICE, "realpath failed on %s: %m", path); 3405137145Syar return; 3406137145Syar } 3407137145Syar snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", 34086740Sguido ctime(&now)+4, ident, remotehost, 3409137145Syar path, (intmax_t)size, 341082792Sache (long)(now - start + (now == start))); 34116740Sguido write(statfd, buf, strlen(buf)); 34126740Sguido } 34136740Sguido} 3414100486Syar 3415100486Syarstatic char * 3416100486Syardoublequote(char *s) 3417100486Syar{ 3418100486Syar int n; 3419100486Syar char *p, *s2; 3420100486Syar 3421100486Syar for (p = s, n = 0; *p; p++) 3422100486Syar if (*p == '"') 3423100486Syar n++; 3424100486Syar 3425100486Syar if ((s2 = malloc(p - s + n + 1)) == NULL) 3426100486Syar return (NULL); 3427100486Syar 3428100486Syar for (p = s2; *s; s++, p++) { 3429100486Syar if ((*p = *s) == '"') 3430100486Syar *(++p) = '"'; 3431100486Syar } 3432100486Syar *p = '\0'; 3433100486Syar 3434100486Syar return (s2); 3435100486Syar} 3436120059Sume 3437120059Sume/* setup server socket for specified address family */ 3438120059Sume/* if af is PF_UNSPEC more than one socket may be returned */ 3439120059Sume/* the returned list is dynamically allocated, so caller needs to free it */ 3440120059Sumestatic int * 3441120059Sumesocksetup(int af, char *bindname, const char *bindport) 3442120059Sume{ 3443120059Sume struct addrinfo hints, *res, *r; 3444120059Sume int error, maxs, *s, *socks; 3445120059Sume const int on = 1; 3446120059Sume 3447120059Sume memset(&hints, 0, sizeof(hints)); 3448120059Sume hints.ai_flags = AI_PASSIVE; 3449120059Sume hints.ai_family = af; 3450120059Sume hints.ai_socktype = SOCK_STREAM; 3451120059Sume error = getaddrinfo(bindname, bindport, &hints, &res); 3452120059Sume if (error) { 3453120059Sume syslog(LOG_ERR, "%s", gai_strerror(error)); 3454120059Sume if (error == EAI_SYSTEM) 3455120059Sume syslog(LOG_ERR, "%s", strerror(errno)); 3456120059Sume return NULL; 3457120059Sume } 3458120059Sume 3459120059Sume /* Count max number of sockets we may open */ 3460120059Sume for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 3461120059Sume ; 3462120059Sume socks = malloc((maxs + 1) * sizeof(int)); 3463120059Sume if (!socks) { 3464120059Sume freeaddrinfo(res); 3465120059Sume syslog(LOG_ERR, "couldn't allocate memory for sockets"); 3466120059Sume return NULL; 3467120059Sume } 3468120059Sume 3469120059Sume *socks = 0; /* num of sockets counter at start of array */ 3470120059Sume s = socks + 1; 3471120059Sume for (r = res; r; r = r->ai_next) { 3472120059Sume *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 3473120059Sume if (*s < 0) { 3474120059Sume syslog(LOG_DEBUG, "control socket: %m"); 3475120059Sume continue; 3476120059Sume } 3477120059Sume if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 3478120059Sume &on, sizeof(on)) < 0) 3479120059Sume syslog(LOG_WARNING, 3480120059Sume "control setsockopt (SO_REUSEADDR): %m"); 3481120059Sume if (r->ai_family == AF_INET6) { 3482120059Sume if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 3483120059Sume &on, sizeof(on)) < 0) 3484120059Sume syslog(LOG_WARNING, 3485120059Sume "control setsockopt (IPV6_V6ONLY): %m"); 3486120059Sume } 3487120059Sume if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 3488120059Sume syslog(LOG_DEBUG, "control bind: %m"); 3489120059Sume close(*s); 3490120059Sume continue; 3491120059Sume } 3492120059Sume (*socks)++; 3493120059Sume s++; 3494120059Sume } 3495120059Sume 3496120059Sume if (res) 3497120059Sume freeaddrinfo(res); 3498120059Sume 3499120059Sume if (*socks == 0) { 3500120059Sume syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); 3501120059Sume free(socks); 3502120059Sume return NULL; 3503120059Sume } 3504120059Sume return(socks); 3505120059Sume} 3506