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. 13262435Sbrueffer * 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$"); 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 961592Srgrimes#include "pathnames.h" 971592Srgrimes#include "extern.h" 981592Srgrimes 991592Srgrimes#include <stdarg.h> 1001592Srgrimes 10125165Sdavidnstatic char version[] = "Version 6.00LS"; 10225165Sdavidn#undef main 1031592Srgrimes 10456668Sshinunion sockunion ctrl_addr; 10556668Sshinunion sockunion data_source; 10656668Sshinunion sockunion data_dest; 10756668Sshinunion sockunion his_addr; 10856668Sshinunion sockunion pasv_addr; 1091592Srgrimes 11015196Sdgint daemon_mode; 1111592Srgrimesint data; 112109742Syarint dataport; 113110037Syarint hostinfo = 1; /* print host-specific info in messages */ 1141592Srgrimesint logged_in; 1151592Srgrimesstruct passwd *pw; 116110036Syarchar *homedir; 11776096Smarkmint ftpdebug; 1181592Srgrimesint timeout = 900; /* timeout after 15 minutes of inactivity */ 1191592Srgrimesint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 1201592Srgrimesint logging; 1219933Spstint restricted_data_ports = 1; 12217435Spstint paranoid = 1; /* be extra careful about security */ 12320042Storstenbint anon_only = 0; /* Only anonymous ftp allowed */ 124168849Syarint assumeutf8 = 0; /* Assume that server file names are in UTF-8 */ 1251592Srgrimesint guest; 12617435Spstint dochroot; 127137862Syarchar *chrootdir; 128102311Syarint dowtmp = 1; 1296740Sguidoint stats; 1306740Sguidoint statfd = -1; 1311592Srgrimesint type; 1321592Srgrimesint form; 1331592Srgrimesint stru; /* avoid C keyword */ 1341592Srgrimesint mode; 1351592Srgrimesint usedefault = 1; /* for data transfers */ 1361592Srgrimesint pdata = -1; /* for passive mode */ 137137861Syarint readonly = 0; /* Server is in readonly mode. */ 138137861Syarint noepsv = 0; /* EPSV command is disabled. */ 139137861Syarint noretr = 0; /* RETR command is disabled. */ 140137861Syarint noguestretr = 0; /* RETR command is disabled for anon users. */ 141137861Syarint noguestmkd = 0; /* MKD command is disabled for anon users. */ 142137861Syarint noguestmod = 1; /* anon users may not modify existing files. */ 14382460Snik 1441592Srgrimesoff_t file_size; 1451592Srgrimesoff_t byte_count; 1461592Srgrimes#if !defined(CMASK) || CMASK == 0 1471592Srgrimes#undef CMASK 1481592Srgrimes#define CMASK 027 1491592Srgrimes#endif 1501592Srgrimesint defumask = CMASK; /* default umask value */ 1511592Srgrimeschar tmpline[7]; 15227650Sdavidnchar *hostname; 15378153Sddint epsvall = 0; 15478153Sdd 15525283Sdavidn#ifdef VIRTUAL_HOSTING 15625283Sdavidnchar *ftpuser; 15725283Sdavidn 15825283Sdavidnstatic struct ftphost { 15925283Sdavidn struct ftphost *next; 16057124Sshin struct addrinfo *hostinfo; 16125283Sdavidn char *hostname; 16225283Sdavidn char *anonuser; 16325283Sdavidn char *statfile; 16425283Sdavidn char *welcome; 16525283Sdavidn char *loginmsg; 16625283Sdavidn} *thishost, *firsthost; 16725283Sdavidn 16825283Sdavidn#endif 169137983Syarchar remotehost[NI_MAXHOST]; 1706740Sguidochar *ident = NULL; 17117435Spst 172202209Sedstatic char wtmpid[20]; 17317435Spst 17474874Smarkm#ifdef USE_PAM 17590148Simpstatic int auth_pam(struct passwd**, const char*); 176137861Syarpam_handle_t *pamh = NULL; 17788763Sache#endif 17879469Smarkm 179137861Syarstatic struct opie opiedata; 180137861Syarstatic char opieprompt[OPIE_CHALLENGE_MAX+1]; 181137861Syarstatic int pwok; 18217478Smarkm 183154630Syarchar *pid_file = NULL; /* means default location to pidfile(3) */ 18417483Sjulian 1851592Srgrimes/* 18674470Sjlemon * Limit number of pathnames that glob can return. 18774470Sjlemon * A limit of 0 indicates the number of pathnames is unlimited. 18874470Sjlemon */ 18974470Sjlemon#define MAXGLOBARGS 16384 19074470Sjlemon# 19174470Sjlemon 19274470Sjlemon/* 1931592Srgrimes * Timeout intervals for retrying connections 1941592Srgrimes * to hosts that don't accept PORT cmds. This 1951592Srgrimes * is a kludge, but given the problems with TCP... 1961592Srgrimes */ 1971592Srgrimes#define SWAITMAX 90 /* wait at most 90 seconds */ 1981592Srgrimes#define SWAITINT 5 /* interval between retries */ 1991592Srgrimes 2001592Srgrimesint swaitmax = SWAITMAX; 2011592Srgrimesint swaitint = SWAITINT; 2021592Srgrimes 2031592Srgrimes#ifdef SETPROCTITLE 20413139Speter#ifdef OLD_SETPROCTITLE 2051592Srgrimeschar **Argv = NULL; /* pointer to argument vector */ 2061592Srgrimeschar *LastArgv = NULL; /* end of argv */ 20713139Speter#endif /* OLD_SETPROCTITLE */ 2081592Srgrimeschar proctitle[LINE_MAX]; /* initial part of title */ 2091592Srgrimes#endif /* SETPROCTITLE */ 2101592Srgrimes 211137848Syar#define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1) 212137848Syar#define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1) 213137848Syar#define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt)) 2141592Srgrimes 215140472Syarstatic volatile sig_atomic_t recvurg; 216140472Syarstatic int transflag; /* NB: for debugging only */ 217140472Syar 218140472Syar#define STARTXFER flagxfer(1) 219140472Syar#define ENDXFER flagxfer(0) 220140472Syar 221140472Syar#define START_UNSAFE maskurg(1) 222140472Syar#define END_UNSAFE maskurg(0) 223140472Syar 224140472Syar/* It's OK to put an `else' clause after this macro. */ 225140472Syar#define CHECKOOB(action) \ 226140472Syar if (recvurg) { \ 227140472Syar recvurg = 0; \ 228140472Syar if (myoob()) { \ 229140472Syar ENDXFER; \ 230140472Syar action; \ 231140472Syar } \ 232140472Syar } 233140472Syar 23425283Sdavidn#ifdef VIRTUAL_HOSTING 235156156Sumestatic void inithosts(int); 236137861Syarstatic void selecthost(union sockunion *); 23725283Sdavidn#endif 23890148Simpstatic void ack(char *); 23990148Simpstatic void sigurg(int); 240140472Syarstatic void maskurg(int); 241140472Syarstatic void flagxfer(int); 242140472Syarstatic int myoob(void); 243216932Scsjpstatic int checkuser(char *, char *, int, char **, int *); 24490148Simpstatic FILE *dataconn(char *, off_t, char *); 24590148Simpstatic void dolog(struct sockaddr *); 24690148Simpstatic void end_login(void); 24790148Simpstatic FILE *getdatasock(char *); 248101537Syarstatic int guniquefd(char *, char **); 24990148Simpstatic void lostconn(int); 25090148Simpstatic void sigquit(int); 25190148Simpstatic int receive_data(FILE *, FILE *); 252137660Syarstatic int send_data(FILE *, FILE *, size_t, off_t, int); 2531592Srgrimesstatic struct passwd * 25490148Simp sgetpwnam(char *); 25590148Simpstatic char *sgetsave(char *); 25690148Simpstatic void reapchild(int); 257137851Syarstatic void appendf(char **, char *, ...) __printflike(2, 3); 258137848Syarstatic void logcmd(char *, char *, char *, off_t); 25990148Simpstatic void logxfer(char *, off_t, time_t); 260100486Syarstatic char *doublequote(char *); 261120059Sumestatic int *socksetup(int, char *, const char *); 2621592Srgrimes 2631592Srgrimesint 26490148Simpmain(int argc, char *argv[], char **envp) 2651592Srgrimes{ 266141918Sstefanf socklen_t addrlen; 267141918Sstefanf int ch, on = 1, tos; 2681592Srgrimes char *cp, line[LINE_MAX]; 2691592Srgrimes FILE *fd; 27056668Sshin char *bindname = NULL; 271109742Syar const char *bindport = "ftp"; 27256668Sshin int family = AF_UNSPEC; 27389935Syar struct sigaction sa; 2741592Srgrimes 27536105Sache tzset(); /* in case no timezone database in ~ftp */ 27689935Syar sigemptyset(&sa.sa_mask); 27789935Syar sa.sa_flags = SA_RESTART; 27836105Sache 27913139Speter#ifdef OLD_SETPROCTITLE 2801592Srgrimes /* 2811592Srgrimes * Save start and extent of argv for setproctitle. 2821592Srgrimes */ 2831592Srgrimes Argv = argv; 2841592Srgrimes while (*envp) 2851592Srgrimes envp++; 2861592Srgrimes LastArgv = envp[-1] + strlen(envp[-1]); 28713139Speter#endif /* OLD_SETPROCTITLE */ 2881592Srgrimes 289138747Syar /* 290138747Syar * Prevent diagnostic messages from appearing on stderr. 291138747Syar * We run as a daemon or from inetd; in both cases, there's 292138747Syar * more reason in logging to syslog. 293138747Syar */ 294138747Syar (void) freopen(_PATH_DEVNULL, "w", stderr); 295138747Syar opterr = 0; 2966740Sguido 297138747Syar /* 298138747Syar * LOG_NDELAY sets up the logging connection immediately, 299138747Syar * necessary for anonymous ftp's that chroot and can't do it later. 300138747Syar */ 301138747Syar openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 302138747Syar 303110037Syar while ((ch = getopt(argc, argv, 304168849Syar "468a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) { 3051592Srgrimes switch (ch) { 306100717Syar case '4': 307120059Sume family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; 30815196Sdg break; 30915196Sdg 310100717Syar case '6': 311120059Sume family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; 312100717Syar break; 313100717Syar 314168849Syar case '8': 315168849Syar assumeutf8 = 1; 316168849Syar break; 317168849Syar 318100717Syar case 'a': 319100717Syar bindname = optarg; 320100717Syar break; 321100717Syar 322100717Syar case 'A': 323100717Syar anon_only = 1; 324100717Syar break; 325100717Syar 3261592Srgrimes case 'd': 32776096Smarkm ftpdebug++; 3281592Srgrimes break; 3291592Srgrimes 330100717Syar case 'D': 331100717Syar daemon_mode++; 332100717Syar break; 333100717Syar 33470102Sphk case 'E': 33570102Sphk noepsv = 1; 33670102Sphk break; 33770102Sphk 338110037Syar case 'h': 339110037Syar hostinfo = 0; 340110037Syar break; 341110037Syar 3421592Srgrimes case 'l': 3431592Srgrimes logging++; /* > 1 == extra logging */ 3441592Srgrimes break; 3451592Srgrimes 346101537Syar case 'm': 347101537Syar noguestmod = 0; 348101537Syar break; 349101537Syar 350100717Syar case 'M': 351100717Syar noguestmkd = 1; 352100717Syar break; 353100717Syar 354100717Syar case 'o': 355100717Syar noretr = 1; 356100717Syar break; 357100717Syar 358100717Syar case 'O': 359100717Syar noguestretr = 1; 360100717Syar break; 361100717Syar 362100717Syar case 'p': 363100717Syar pid_file = optarg; 364100717Syar break; 365100717Syar 366109742Syar case 'P': 367109742Syar bindport = optarg; 368109742Syar break; 369109742Syar 37070102Sphk case 'r': 37170102Sphk readonly = 1; 37270102Sphk break; 37370102Sphk 37417435Spst case 'R': 37517435Spst paranoid = 0; 3769933Spst break; 3779933Spst 3786740Sguido case 'S': 37917435Spst stats++; 3806740Sguido break; 38117435Spst 38217435Spst case 't': 38317435Spst timeout = atoi(optarg); 38417435Spst if (maxtimeout < timeout) 38517435Spst maxtimeout = timeout; 38617435Spst break; 38717435Spst 388100717Syar case 'T': 389100717Syar maxtimeout = atoi(optarg); 390100717Syar if (timeout > maxtimeout) 391100717Syar timeout = maxtimeout; 39217435Spst break; 39317435Spst 3941592Srgrimes case 'u': 3951592Srgrimes { 3961592Srgrimes long val = 0; 3971592Srgrimes 3981592Srgrimes val = strtol(optarg, &optarg, 8); 3991592Srgrimes if (*optarg != '\0' || val < 0) 400138747Syar syslog(LOG_WARNING, "bad value for -u"); 4011592Srgrimes else 4021592Srgrimes defumask = val; 4031592Srgrimes break; 4041592Srgrimes } 405100717Syar case 'U': 406100717Syar restricted_data_ports = 0; 40720042Storstenb break; 4081592Srgrimes 4091592Srgrimes case 'v': 410100720Syar ftpdebug++; 4111592Srgrimes break; 4121592Srgrimes 413102311Syar case 'W': 414102311Syar dowtmp = 0; 415102311Syar break; 416102311Syar 4171592Srgrimes default: 418138747Syar syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); 4191592Srgrimes break; 4201592Srgrimes } 4211592Srgrimes } 42215196Sdg 42315196Sdg if (daemon_mode) { 424120059Sume int *ctl_sock, fd, maxfd = -1, nfds, i; 425120059Sume fd_set defreadfds, readfds; 426120059Sume pid_t pid; 427154630Syar struct pidfh *pfh; 42815196Sdg 429154630Syar if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) { 430154630Syar if (errno == EEXIST) { 431154630Syar syslog(LOG_ERR, "%s already running, pid %d", 432154630Syar getprogname(), (int)pid); 433154630Syar exit(1); 434154630Syar } 435154630Syar syslog(LOG_WARNING, "pidfile_open: %m"); 436154630Syar } 437154630Syar 43815196Sdg /* 43915196Sdg * Detach from parent. 44015196Sdg */ 44115196Sdg if (daemon(1, 1) < 0) { 44215196Sdg syslog(LOG_ERR, "failed to become a daemon"); 44315196Sdg exit(1); 44415196Sdg } 445154630Syar 446154630Syar if (pfh != NULL && pidfile_write(pfh) == -1) 447154630Syar syslog(LOG_WARNING, "pidfile_write: %m"); 448154630Syar 44989935Syar sa.sa_handler = reapchild; 45089935Syar (void)sigaction(SIGCHLD, &sa, NULL); 45156668Sshin 452156156Sume#ifdef VIRTUAL_HOSTING 453156156Sume inithosts(family); 454156156Sume#endif 455156156Sume 45615196Sdg /* 45715196Sdg * Open a socket, bind it to the FTP port, and start 45815196Sdg * listening. 45915196Sdg */ 460120059Sume ctl_sock = socksetup(family, bindname, bindport); 461120059Sume if (ctl_sock == NULL) 46215196Sdg exit(1); 463120059Sume 464120059Sume FD_ZERO(&defreadfds); 465120059Sume for (i = 1; i <= *ctl_sock; i++) { 466120059Sume FD_SET(ctl_sock[i], &defreadfds); 467120059Sume if (listen(ctl_sock[i], 32) < 0) { 468120059Sume syslog(LOG_ERR, "control listen: %m"); 469120059Sume exit(1); 470120059Sume } 471120059Sume if (maxfd < ctl_sock[i]) 472120059Sume maxfd = ctl_sock[i]; 47315196Sdg } 474120059Sume 47515196Sdg /* 47615196Sdg * Loop forever accepting connection requests and forking off 47715196Sdg * children to handle them. 47815196Sdg */ 47915196Sdg while (1) { 480120059Sume FD_COPY(&defreadfds, &readfds); 481120059Sume nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); 482120059Sume if (nfds <= 0) { 483120059Sume if (nfds < 0 && errno != EINTR) 484120059Sume syslog(LOG_WARNING, "select: %m"); 485120059Sume continue; 486120059Sume } 487120059Sume 488120059Sume pid = -1; 489120059Sume for (i = 1; i <= *ctl_sock; i++) 490120059Sume if (FD_ISSET(ctl_sock[i], &readfds)) { 491120059Sume addrlen = sizeof(his_addr); 492120059Sume fd = accept(ctl_sock[i], 493120059Sume (struct sockaddr *)&his_addr, 494120059Sume &addrlen); 495154634Syar if (fd == -1) { 496154634Syar syslog(LOG_WARNING, 497154634Syar "accept: %m"); 498154634Syar continue; 499135737Smaxim } 500154634Syar switch (pid = fork()) { 501154634Syar case 0: 502154634Syar /* child */ 503154634Syar (void) dup2(fd, 0); 504154634Syar (void) dup2(fd, 1); 505154634Syar (void) close(fd); 506154634Syar for (i = 1; i <= *ctl_sock; i++) 507154634Syar close(ctl_sock[i]); 508154634Syar if (pfh != NULL) 509154634Syar pidfile_close(pfh); 510154634Syar goto gotchild; 511154634Syar case -1: 512154634Syar syslog(LOG_WARNING, "fork: %m"); 513154634Syar /* FALLTHROUGH */ 514154634Syar default: 515154634Syar close(fd); 516154634Syar } 517120059Sume } 51815196Sdg } 51915196Sdg } else { 52015196Sdg addrlen = sizeof(his_addr); 52115196Sdg if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 52215196Sdg syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 52315196Sdg exit(1); 52415196Sdg } 525156156Sume 526156156Sume#ifdef VIRTUAL_HOSTING 527156156Sume if (his_addr.su_family == AF_INET6 && 528156156Sume IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 529156156Sume family = AF_INET; 530156156Sume else 531156156Sume family = his_addr.su_family; 532156156Sume inithosts(family); 533156156Sume#endif 53415196Sdg } 53515196Sdg 536154634Syargotchild: 53789935Syar sa.sa_handler = SIG_DFL; 53889935Syar (void)sigaction(SIGCHLD, &sa, NULL); 5391592Srgrimes 54089935Syar sa.sa_handler = sigurg; 54189935Syar sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ 54289935Syar (void)sigaction(SIGURG, &sa, NULL); 54389935Syar 54489935Syar sigfillset(&sa.sa_mask); /* block all signals in handler */ 54589935Syar sa.sa_flags = SA_RESTART; 54689935Syar sa.sa_handler = sigquit; 54789935Syar (void)sigaction(SIGHUP, &sa, NULL); 54889935Syar (void)sigaction(SIGINT, &sa, NULL); 54989935Syar (void)sigaction(SIGQUIT, &sa, NULL); 55089935Syar (void)sigaction(SIGTERM, &sa, NULL); 55189935Syar 55289935Syar sa.sa_handler = lostconn; 55389935Syar (void)sigaction(SIGPIPE, &sa, NULL); 55489935Syar 55515196Sdg addrlen = sizeof(ctrl_addr); 55615196Sdg if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 55715196Sdg syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 55815196Sdg exit(1); 55915196Sdg } 560109742Syar dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ 56125283Sdavidn#ifdef VIRTUAL_HOSTING 56225283Sdavidn /* select our identity from virtual host table */ 56356668Sshin selecthost(&ctrl_addr); 56425283Sdavidn#endif 56515196Sdg#ifdef IP_TOS 56656668Sshin if (ctrl_addr.su_family == AF_INET) 56756668Sshin { 56815196Sdg tos = IPTOS_LOWDELAY; 569100612Syar if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 570100609Syar syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); 57156668Sshin } 57215196Sdg#endif 57335482Sdg /* 57435482Sdg * Disable Nagle on the control channel so that we don't have to wait 57535482Sdg * for peer's ACK before issuing our next reply. 57635482Sdg */ 57735482Sdg if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 578100609Syar syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); 57935482Sdg 58056668Sshin data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); 58115196Sdg 582202209Sed (void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid()); 58317435Spst 5841592Srgrimes /* Try to handle urgent data inline */ 5851592Srgrimes#ifdef SO_OOBINLINE 586100612Syar if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) 587100609Syar syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); 5881592Srgrimes#endif 5891592Srgrimes 5901592Srgrimes#ifdef F_SETOWN 5911592Srgrimes if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 5921592Srgrimes syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 5931592Srgrimes#endif 59456668Sshin dolog((struct sockaddr *)&his_addr); 5951592Srgrimes /* 5961592Srgrimes * Set up default state 5971592Srgrimes */ 5981592Srgrimes data = -1; 5991592Srgrimes type = TYPE_A; 6001592Srgrimes form = FORM_N; 6011592Srgrimes stru = STRU_F; 6021592Srgrimes mode = MODE_S; 6031592Srgrimes tmpline[0] = '\0'; 6041592Srgrimes 6051592Srgrimes /* If logins are disabled, print out the message. */ 6061592Srgrimes if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 6071592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6081592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6091592Srgrimes *cp = '\0'; 6101592Srgrimes lreply(530, "%s", line); 6111592Srgrimes } 6121592Srgrimes (void) fflush(stdout); 6131592Srgrimes (void) fclose(fd); 6141592Srgrimes reply(530, "System not available."); 6151592Srgrimes exit(0); 6161592Srgrimes } 61725283Sdavidn#ifdef VIRTUAL_HOSTING 618130428Sobrien fd = fopen(thishost->welcome, "r"); 61925283Sdavidn#else 620130428Sobrien fd = fopen(_PATH_FTPWELCOME, "r"); 62125283Sdavidn#endif 622130428Sobrien if (fd != NULL) { 6231592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6241592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6251592Srgrimes *cp = '\0'; 6261592Srgrimes lreply(220, "%s", line); 6271592Srgrimes } 6281592Srgrimes (void) fflush(stdout); 6291592Srgrimes (void) fclose(fd); 6301592Srgrimes /* reply(220,) must follow */ 6311592Srgrimes } 63225283Sdavidn#ifndef VIRTUAL_HOSTING 63327650Sdavidn if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 63476096Smarkm fatalerror("Ran out of memory."); 635137983Syar if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 636137983Syar hostname[0] = '\0'; 63745422Sbrian hostname[MAXHOSTNAMELEN - 1] = '\0'; 63825283Sdavidn#endif 639110037Syar if (hostinfo) 640110037Syar reply(220, "%s FTP server (%s) ready.", hostname, version); 641110037Syar else 642110037Syar reply(220, "FTP server ready."); 6431592Srgrimes for (;;) 6441592Srgrimes (void) yyparse(); 6451592Srgrimes /* NOTREACHED */ 6461592Srgrimes} 6471592Srgrimes 6481592Srgrimesstatic void 64990148Simplostconn(int signo) 6501592Srgrimes{ 6511592Srgrimes 65276096Smarkm if (ftpdebug) 6531592Srgrimes syslog(LOG_DEBUG, "lost connection"); 65431329Scharnier dologout(1); 6551592Srgrimes} 6561592Srgrimes 65789935Syarstatic void 65890148Simpsigquit(int signo) 65989935Syar{ 66089935Syar 66189935Syar syslog(LOG_ERR, "got signal %d", signo); 66289935Syar dologout(1); 66389935Syar} 66489935Syar 66525283Sdavidn#ifdef VIRTUAL_HOSTING 6661592Srgrimes/* 66725283Sdavidn * read in virtual host tables (if they exist) 66825283Sdavidn */ 66925283Sdavidn 67025283Sdavidnstatic void 671156156Sumeinithosts(int family) 67225283Sdavidn{ 673100182Syar int insert; 67499877Syar size_t len; 67525283Sdavidn FILE *fp; 67699877Syar char *cp, *mp, *line; 67799877Syar char *hostname; 678100182Syar char *vhost, *anonuser, *statfile, *welcome, *loginmsg; 67925283Sdavidn struct ftphost *hrp, *lhrp; 68056668Sshin struct addrinfo hints, *res, *ai; 68125283Sdavidn 68225283Sdavidn /* 68325283Sdavidn * Fill in the default host information 68425283Sdavidn */ 68599877Syar if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 68676096Smarkm fatalerror("Ran out of memory."); 687137983Syar if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 68899877Syar hostname[0] = '\0'; 68999877Syar hostname[MAXHOSTNAMELEN - 1] = '\0'; 69099877Syar if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 69199877Syar fatalerror("Ran out of memory."); 69299877Syar hrp->hostname = hostname; 69357124Sshin hrp->hostinfo = NULL; 69456668Sshin 69556668Sshin memset(&hints, 0, sizeof(hints)); 696156156Sume hints.ai_flags = AI_PASSIVE; 697156156Sume hints.ai_family = family; 698156156Sume hints.ai_socktype = SOCK_STREAM; 699102183Syar if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) 70057124Sshin hrp->hostinfo = res; 70125283Sdavidn hrp->statfile = _PATH_FTPDSTATFILE; 70225283Sdavidn hrp->welcome = _PATH_FTPWELCOME; 70325283Sdavidn hrp->loginmsg = _PATH_FTPLOGINMESG; 70425283Sdavidn hrp->anonuser = "ftp"; 70525283Sdavidn hrp->next = NULL; 70625283Sdavidn thishost = firsthost = lhrp = hrp; 70725283Sdavidn if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { 708102474Syar int addrsize, gothost; 70956668Sshin void *addr; 71056668Sshin struct hostent *hp; 71156668Sshin 71299877Syar while ((line = fgetln(fp, &len)) != NULL) { 71356668Sshin int i, hp_error; 71425283Sdavidn 71599877Syar /* skip comments */ 71699877Syar if (line[0] == '#') 71725283Sdavidn continue; 71899877Syar if (line[len - 1] == '\n') { 71999877Syar line[len - 1] = '\0'; 72099877Syar mp = NULL; 72199877Syar } else { 72299877Syar if ((mp = malloc(len + 1)) == NULL) 72399877Syar fatalerror("Ran out of memory."); 72499877Syar memcpy(mp, line, len); 72599877Syar mp[len] = '\0'; 72699877Syar line = mp; 72725283Sdavidn } 72825283Sdavidn cp = strtok(line, " \t"); 72999877Syar /* skip empty lines */ 73099877Syar if (cp == NULL) 73199877Syar goto nextline; 732100182Syar vhost = cp; 73356668Sshin 734100182Syar /* set defaults */ 735100182Syar anonuser = "ftp"; 736100182Syar statfile = _PATH_FTPDSTATFILE; 737100182Syar welcome = _PATH_FTPWELCOME; 738100182Syar loginmsg = _PATH_FTPLOGINMESG; 739100182Syar 740100182Syar /* 741100182Syar * Preparse the line so we can use its info 742100182Syar * for all the addresses associated with 743100182Syar * the virtual host name. 744100182Syar * Field 0, the virtual host name, is special: 745100182Syar * it's already parsed off and will be strdup'ed 746100182Syar * later, after we know its canonical form. 747100182Syar */ 748100182Syar for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) 749100182Syar if (*cp != '-' && (cp = strdup(cp))) 750100182Syar switch (i) { 751100182Syar case 1: /* anon user permissions */ 752100182Syar anonuser = cp; 753100182Syar break; 754100182Syar case 2: /* statistics file */ 755100182Syar statfile = cp; 756100182Syar break; 757100182Syar case 3: /* welcome message */ 758100182Syar welcome = cp; 759100182Syar break; 760100182Syar case 4: /* login message */ 761100182Syar loginmsg = cp; 762100182Syar break; 763100182Syar default: /* programming error */ 764100182Syar abort(); 765100182Syar /* NOTREACHED */ 766100182Syar } 767100182Syar 76856668Sshin hints.ai_flags = AI_PASSIVE; 769156156Sume hints.ai_family = family; 770156156Sume hints.ai_socktype = SOCK_STREAM; 771102183Syar if (getaddrinfo(vhost, NULL, &hints, &res) != 0) 77299877Syar goto nextline; 77356668Sshin for (ai = res; ai != NULL && ai->ai_addr != NULL; 77462100Sdavidn ai = ai->ai_next) { 77556668Sshin 77662100Sdavidn gothost = 0; 77725283Sdavidn for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { 77857124Sshin struct addrinfo *hi; 77957124Sshin 78057124Sshin for (hi = hrp->hostinfo; hi != NULL; 78157124Sshin hi = hi->ai_next) 78257124Sshin if (hi->ai_addrlen == ai->ai_addrlen && 78357124Sshin memcmp(hi->ai_addr, 78457124Sshin ai->ai_addr, 78562100Sdavidn ai->ai_addr->sa_len) == 0) { 78662100Sdavidn gothost++; 78757124Sshin break; 788100183Syar } 78962100Sdavidn if (gothost) 79062100Sdavidn break; 79125283Sdavidn } 79225283Sdavidn if (hrp == NULL) { 79325283Sdavidn if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 79499877Syar goto nextline; 795102183Syar hrp->hostname = NULL; 796100182Syar insert = 1; 797102473Syar } else { 798106754Syar if (hrp->hostinfo && hrp->hostinfo != res) 799102473Syar freeaddrinfo(hrp->hostinfo); 800100182Syar insert = 0; /* host already in the chain */ 801102473Syar } 80257124Sshin hrp->hostinfo = res; 80357124Sshin 80425283Sdavidn /* 80525283Sdavidn * determine hostname to use. 80656668Sshin * force defined name if there is a valid alias 80725283Sdavidn * otherwise fallback to primary hostname 80825283Sdavidn */ 80956668Sshin /* XXX: getaddrinfo() can't do alias check */ 81057124Sshin switch(hrp->hostinfo->ai_family) { 81156668Sshin case AF_INET: 812100259Syar addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; 813100259Syar addrsize = sizeof(struct in_addr); 81456668Sshin break; 81556668Sshin case AF_INET6: 816100259Syar addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; 817100259Syar addrsize = sizeof(struct in6_addr); 81856668Sshin break; 81956668Sshin default: 82056668Sshin /* should not reach here */ 821102473Syar freeaddrinfo(hrp->hostinfo); 822102473Syar if (insert) 823102473Syar free(hrp); /*not in chain, can free*/ 824102473Syar else 825102473Syar hrp->hostinfo = NULL; /*mark as blank*/ 82699877Syar goto nextline; 82756668Sshin /* NOTREACHED */ 82856668Sshin } 829100612Syar if ((hp = getipnodebyaddr(addr, addrsize, 83057124Sshin hrp->hostinfo->ai_family, 83156668Sshin &hp_error)) != NULL) { 832100182Syar if (strcmp(vhost, hp->h_name) != 0) { 83325283Sdavidn if (hp->h_aliases == NULL) 834100182Syar vhost = hp->h_name; 83525283Sdavidn else { 83625283Sdavidn i = 0; 83725283Sdavidn while (hp->h_aliases[i] && 838100182Syar strcmp(vhost, hp->h_aliases[i]) != 0) 83925283Sdavidn ++i; 84025283Sdavidn if (hp->h_aliases[i] == NULL) 841100182Syar vhost = hp->h_name; 84225283Sdavidn } 84325283Sdavidn } 84425283Sdavidn } 845102183Syar if (hrp->hostname && 846102183Syar strcmp(hrp->hostname, vhost) != 0) { 847102183Syar free(hrp->hostname); 848102183Syar hrp->hostname = NULL; 849102183Syar } 850102183Syar if (hrp->hostname == NULL && 851102473Syar (hrp->hostname = strdup(vhost)) == NULL) { 852102473Syar freeaddrinfo(hrp->hostinfo); 853102473Syar hrp->hostinfo = NULL; /* mark as blank */ 854102473Syar if (hp) 855102473Syar freehostent(hp); 856100182Syar goto nextline; 857102473Syar } 858100182Syar hrp->anonuser = anonuser; 859100182Syar hrp->statfile = statfile; 860100182Syar hrp->welcome = welcome; 861100182Syar hrp->loginmsg = loginmsg; 862100182Syar if (insert) { 863100182Syar hrp->next = NULL; 864100182Syar lhrp->next = hrp; 865100182Syar lhrp = hrp; 866100182Syar } 867100263Syar if (hp) 868100263Syar freehostent(hp); 86956668Sshin } 87099877Syarnextline: 87199877Syar if (mp) 87299877Syar free(mp); 87325283Sdavidn } 87425283Sdavidn (void) fclose(fp); 87525283Sdavidn } 87625283Sdavidn} 87725283Sdavidn 87825283Sdavidnstatic void 87990148Simpselecthost(union sockunion *su) 88025283Sdavidn{ 88125283Sdavidn struct ftphost *hrp; 88256668Sshin u_int16_t port; 88356668Sshin#ifdef INET6 88456668Sshin struct in6_addr *mapped_in6 = NULL; 88556668Sshin#endif 88657124Sshin struct addrinfo *hi; 88725283Sdavidn 88856668Sshin#ifdef INET6 88956668Sshin /* 89056668Sshin * XXX IPv4 mapped IPv6 addr consideraton, 89156668Sshin * specified in rfc2373. 89256668Sshin */ 89356668Sshin if (su->su_family == AF_INET6 && 89456668Sshin IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) 89556668Sshin mapped_in6 = &su->su_sin6.sin6_addr; 89656668Sshin#endif 89756668Sshin 89825283Sdavidn hrp = thishost = firsthost; /* default */ 89956668Sshin port = su->su_port; 90056668Sshin su->su_port = 0; 90125283Sdavidn while (hrp != NULL) { 90262100Sdavidn for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { 90357124Sshin if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { 90425283Sdavidn thishost = hrp; 905137987Syar goto found; 90625283Sdavidn } 90756668Sshin#ifdef INET6 90856668Sshin /* XXX IPv4 mapped IPv6 addr consideraton */ 90957124Sshin if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && 91056668Sshin (memcmp(&mapped_in6->s6_addr[12], 91157124Sshin &((struct sockaddr_in *)hi->ai_addr)->sin_addr, 91256668Sshin sizeof(struct in_addr)) == 0)) { 91356668Sshin thishost = hrp; 914137987Syar goto found; 91556668Sshin } 91656668Sshin#endif 91762100Sdavidn } 91862100Sdavidn hrp = hrp->next; 91925283Sdavidn } 920137987Syarfound: 92156668Sshin su->su_port = port; 92225283Sdavidn /* setup static variables as appropriate */ 92325283Sdavidn hostname = thishost->hostname; 92425283Sdavidn ftpuser = thishost->anonuser; 92525283Sdavidn} 92625283Sdavidn#endif 92725283Sdavidn 92825283Sdavidn/* 9291592Srgrimes * Helper function for sgetpwnam(). 9301592Srgrimes */ 9311592Srgrimesstatic char * 93290148Simpsgetsave(char *s) 9331592Srgrimes{ 934137659Syar char *new = malloc(strlen(s) + 1); 9351592Srgrimes 9361592Srgrimes if (new == NULL) { 937137852Syar reply(421, "Ran out of memory."); 9381592Srgrimes dologout(1); 9391592Srgrimes /* NOTREACHED */ 9401592Srgrimes } 9411592Srgrimes (void) strcpy(new, s); 9421592Srgrimes return (new); 9431592Srgrimes} 9441592Srgrimes 9451592Srgrimes/* 9461592Srgrimes * Save the result of a getpwnam. Used for USER command, since 9471592Srgrimes * the data returned must not be clobbered by any other command 9481592Srgrimes * (e.g., globbing). 949137076Syar * NB: The data returned by sgetpwnam() will remain valid until 950137076Syar * the next call to this function. Its difference from getpwnam() 951137076Syar * is that sgetpwnam() is known to be called from ftpd code only. 9521592Srgrimes */ 9531592Srgrimesstatic struct passwd * 95490148Simpsgetpwnam(char *name) 9551592Srgrimes{ 9561592Srgrimes static struct passwd save; 9571592Srgrimes struct passwd *p; 9581592Srgrimes 9591592Srgrimes if ((p = getpwnam(name)) == NULL) 9601592Srgrimes return (p); 9611592Srgrimes if (save.pw_name) { 9621592Srgrimes free(save.pw_name); 9631592Srgrimes free(save.pw_passwd); 964262284Sbrueffer free(save.pw_class); 9651592Srgrimes free(save.pw_gecos); 9661592Srgrimes free(save.pw_dir); 9671592Srgrimes free(save.pw_shell); 9681592Srgrimes } 9691592Srgrimes save = *p; 9701592Srgrimes save.pw_name = sgetsave(p->pw_name); 9711592Srgrimes save.pw_passwd = sgetsave(p->pw_passwd); 972262284Sbrueffer save.pw_class = sgetsave(p->pw_class); 9731592Srgrimes save.pw_gecos = sgetsave(p->pw_gecos); 9741592Srgrimes save.pw_dir = sgetsave(p->pw_dir); 9751592Srgrimes save.pw_shell = sgetsave(p->pw_shell); 9761592Srgrimes return (&save); 9771592Srgrimes} 9781592Srgrimes 9791592Srgrimesstatic int login_attempts; /* number of failed login attempts */ 9801592Srgrimesstatic int askpasswd; /* had user command, ask for passwd */ 98164778Ssheldonhstatic char curname[MAXLOGNAME]; /* current USER name */ 9821592Srgrimes 9831592Srgrimes/* 9841592Srgrimes * USER command. 9851592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable; 9861592Srgrimes * sets askpasswd if a PASS command is expected. If logged in previously, 9871592Srgrimes * need to reset state. If name is "ftp" or "anonymous", the name is not in 9881592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 9891592Srgrimes * If account doesn't exist, ask for passwd anyway. Otherwise, check user 9901592Srgrimes * requesting login privileges. Disallow anyone who does not have a standard 9911592Srgrimes * shell as returned by getusershell(). Disallow anyone mentioned in the file 9921592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 9931592Srgrimes */ 9941592Srgrimesvoid 99590148Simpuser(char *name) 9961592Srgrimes{ 997216932Scsjp int ecode; 9981592Srgrimes char *cp, *shell; 9991592Srgrimes 10001592Srgrimes if (logged_in) { 10011592Srgrimes if (guest) { 10021592Srgrimes reply(530, "Can't change user from guest login."); 10031592Srgrimes return; 100417435Spst } else if (dochroot) { 100517435Spst reply(530, "Can't change user from chroot user."); 100617435Spst return; 10071592Srgrimes } 10081592Srgrimes end_login(); 10091592Srgrimes } 10101592Srgrimes 10111592Srgrimes guest = 0; 1012130428Sobrien#ifdef VIRTUAL_HOSTING 1013130428Sobrien pw = sgetpwnam(thishost->anonuser); 1014130428Sobrien#else 1015130428Sobrien pw = sgetpwnam("ftp"); 1016130428Sobrien#endif 10171592Srgrimes if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 1018216932Scsjp if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) || 1019216932Scsjp (ecode != 0 && ecode != ENOENT)) 10201592Srgrimes reply(530, "User %s access denied.", name); 1021216932Scsjp else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) || 1022216932Scsjp (ecode != 0 && ecode != ENOENT)) 1023216932Scsjp reply(530, "User %s access denied.", name); 1024130428Sobrien else if (pw != NULL) { 10251592Srgrimes guest = 1; 10261592Srgrimes askpasswd = 1; 10271592Srgrimes reply(331, 10283938Spst "Guest login ok, send your email address as password."); 10291592Srgrimes } else 10301592Srgrimes reply(530, "User %s unknown.", name); 10311592Srgrimes if (!askpasswd && logging) 10321592Srgrimes syslog(LOG_NOTICE, 10331592Srgrimes "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 10341592Srgrimes return; 10351592Srgrimes } 103620042Storstenb if (anon_only != 0) { 103720042Storstenb reply(530, "Sorry, only anonymous ftp allowed."); 103820042Storstenb return; 103920042Storstenb } 104020042Storstenb 104117478Smarkm if ((pw = sgetpwnam(name))) { 10421592Srgrimes if ((shell = pw->pw_shell) == NULL || *shell == 0) 10431592Srgrimes shell = _PATH_BSHELL; 1044124687Scharnier setusershell(); 10451592Srgrimes while ((cp = getusershell()) != NULL) 10461592Srgrimes if (strcmp(cp, shell) == 0) 10471592Srgrimes break; 10481592Srgrimes endusershell(); 10491592Srgrimes 1050216932Scsjp if (cp == NULL || 1051216932Scsjp (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) || 1052216932Scsjp (ecode != 0 && ecode != ENOENT))) { 10531592Srgrimes reply(530, "User %s access denied.", name); 10541592Srgrimes if (logging) 10551592Srgrimes syslog(LOG_NOTICE, 10561592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 10571592Srgrimes remotehost, name); 1058137811Syar pw = NULL; 10591592Srgrimes return; 10601592Srgrimes } 10611592Srgrimes } 10621592Srgrimes if (logging) 10631592Srgrimes strncpy(curname, name, sizeof(curname)-1); 106488763Sache 106588763Sache pwok = 0; 106679469Smarkm#ifdef USE_PAM 106779469Smarkm /* XXX Kluge! The conversation mechanism needs to be fixed. */ 106888763Sache#endif 106988763Sache if (opiechallenge(&opiedata, name, opieprompt) == 0) { 107088763Sache pwok = (pw != NULL) && 107188763Sache opieaccessfile(remotehost) && 107288763Sache opiealways(pw->pw_dir); 107388763Sache reply(331, "Response to %s %s for %s.", 107488763Sache opieprompt, pwok ? "requested" : "required", name); 107588763Sache } else { 107688763Sache pwok = 1; 107784146Sache reply(331, "Password required for %s.", name); 107888763Sache } 10791592Srgrimes askpasswd = 1; 10801592Srgrimes /* 10811592Srgrimes * Delay before reading passwd after first failed 10821592Srgrimes * attempt to slow down passwd-guessing programs. 10831592Srgrimes */ 10841592Srgrimes if (login_attempts) 1085137659Syar sleep(login_attempts); 10861592Srgrimes} 10871592Srgrimes 10881592Srgrimes/* 1089109893Syar * Check if a user is in the file "fname", 1090109893Syar * return a pointer to a malloc'd string with the rest 1091109893Syar * of the matching line in "residue" if not NULL. 10921592Srgrimes */ 10931592Srgrimesstatic int 1094216932Scsjpcheckuser(char *fname, char *name, int pwset, char **residue, int *ecode) 10951592Srgrimes{ 10961592Srgrimes FILE *fd; 10971592Srgrimes int found = 0; 109899877Syar size_t len; 109999877Syar char *line, *mp, *p; 11001592Srgrimes 1101216932Scsjp if (ecode != NULL) 1102216932Scsjp *ecode = 0; 110317435Spst if ((fd = fopen(fname, "r")) != NULL) { 110499877Syar while (!found && (line = fgetln(fd, &len)) != NULL) { 110599877Syar /* skip comments */ 110699877Syar if (line[0] == '#') 110799877Syar continue; 110899877Syar if (line[len - 1] == '\n') { 110999877Syar line[len - 1] = '\0'; 111099877Syar mp = NULL; 111199877Syar } else { 111299877Syar if ((mp = malloc(len + 1)) == NULL) 111399877Syar fatalerror("Ran out of memory."); 111499877Syar memcpy(mp, line, len); 111599877Syar mp[len] = '\0'; 111699877Syar line = mp; 111799877Syar } 111899877Syar /* avoid possible leading and trailing whitespace */ 111999877Syar p = strtok(line, " \t"); 112099877Syar /* skip empty lines */ 112199877Syar if (p == NULL) 112299877Syar goto nextline; 112399877Syar /* 112499877Syar * if first chr is '@', check group membership 112599877Syar */ 112699877Syar if (p[0] == '@') { 112799877Syar int i = 0; 112899877Syar struct group *grp; 112999877Syar 1130109893Syar if (p[1] == '\0') /* single @ matches anyone */ 113199877Syar found = 1; 1132109893Syar else { 1133109893Syar if ((grp = getgrnam(p+1)) == NULL) 1134109893Syar goto nextline; 1135109893Syar /* 1136109893Syar * Check user's default group 1137109893Syar */ 1138109893Syar if (pwset && grp->gr_gid == pw->pw_gid) 1139109893Syar found = 1; 1140109893Syar /* 1141109893Syar * Check supplementary groups 1142109893Syar */ 1143109893Syar while (!found && grp->gr_mem[i]) 1144109893Syar found = strcmp(name, 1145109893Syar grp->gr_mem[i++]) 1146109893Syar == 0; 1147109893Syar } 11481592Srgrimes } 114999877Syar /* 115099877Syar * Otherwise, just check for username match 115199877Syar */ 115299877Syar else 115399877Syar found = strcmp(p, name) == 0; 1154109893Syar /* 1155109893Syar * Save the rest of line to "residue" if matched 1156109893Syar */ 1157109893Syar if (found && residue) { 1158109938Syar if ((p = strtok(NULL, "")) != NULL) 1159109938Syar p += strspn(p, " \t"); 1160109938Syar if (p && *p) { 1161109893Syar if ((*residue = strdup(p)) == NULL) 1162109893Syar fatalerror("Ran out of memory."); 1163109893Syar } else 1164109893Syar *residue = NULL; 1165109893Syar } 116699877Syarnextline: 116799877Syar if (mp) 116899877Syar free(mp); 116999877Syar } 11701592Srgrimes (void) fclose(fd); 1171216932Scsjp } else if (ecode != NULL) 1172216932Scsjp *ecode = errno; 11731592Srgrimes return (found); 11741592Srgrimes} 11751592Srgrimes 11761592Srgrimes/* 11771592Srgrimes * Terminate login as previous user, if any, resetting state; 11781592Srgrimes * used when USER command is given or login fails. 11791592Srgrimes */ 11801592Srgrimesstatic void 118190148Simpend_login(void) 11821592Srgrimes{ 118374874Smarkm#ifdef USE_PAM 118474874Smarkm int e; 118574874Smarkm#endif 11861592Srgrimes 1187132893Syar (void) seteuid(0); 1188202604Sed if (logged_in && dowtmp) 1189202604Sed ftpd_logwtmp(wtmpid, NULL, NULL); 11901592Srgrimes pw = NULL; 119125101Sdavidn#ifdef LOGIN_CAP 1192223434Strasz setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | 1193223434Strasz LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | 1194223434Strasz LOGIN_SETENV)); 119525101Sdavidn#endif 119674874Smarkm#ifdef USE_PAM 1197137078Syar if (pamh) { 1198137078Syar if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 1199137078Syar syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 1200137078Syar if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 1201137078Syar syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 1202137078Syar if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 1203137078Syar syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 1204137078Syar pamh = NULL; 1205137078Syar } 120674874Smarkm#endif 12071592Srgrimes logged_in = 0; 12081592Srgrimes guest = 0; 120917435Spst dochroot = 0; 12101592Srgrimes} 12111592Srgrimes 121274874Smarkm#ifdef USE_PAM 121351433Smarkm 121451433Smarkm/* 121551433Smarkm * the following code is stolen from imap-uw PAM authentication module and 121651433Smarkm * login.c 121751433Smarkm */ 121851433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL) 121951433Smarkm 122051433Smarkmstruct cred_t { 122151433Smarkm const char *uname; /* user name */ 122251433Smarkm const char *pass; /* password */ 122351433Smarkm}; 122451433Smarkmtypedef struct cred_t cred_t; 122551433Smarkm 122651433Smarkmstatic int 122751433Smarkmauth_conv(int num_msg, const struct pam_message **msg, 122851433Smarkm struct pam_response **resp, void *appdata) 122951433Smarkm{ 123051433Smarkm int i; 123151433Smarkm cred_t *cred = (cred_t *) appdata; 123291244Sdes struct pam_response *reply; 123351433Smarkm 123491244Sdes reply = calloc(num_msg, sizeof *reply); 123591244Sdes if (reply == NULL) 123691244Sdes return PAM_BUF_ERR; 123791244Sdes 123851433Smarkm for (i = 0; i < num_msg; i++) { 123951433Smarkm switch (msg[i]->msg_style) { 124051433Smarkm case PAM_PROMPT_ECHO_ON: /* assume want user name */ 124151433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 124251433Smarkm reply[i].resp = COPY_STRING(cred->uname); 124351433Smarkm /* PAM frees resp. */ 124451433Smarkm break; 124551433Smarkm case PAM_PROMPT_ECHO_OFF: /* assume want password */ 124651433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 124751433Smarkm reply[i].resp = COPY_STRING(cred->pass); 124851433Smarkm /* PAM frees resp. */ 124951433Smarkm break; 125051433Smarkm case PAM_TEXT_INFO: 125151433Smarkm case PAM_ERROR_MSG: 125251433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 125351433Smarkm reply[i].resp = NULL; 125451433Smarkm break; 125551433Smarkm default: /* unknown message style */ 125651433Smarkm free(reply); 125751433Smarkm return PAM_CONV_ERR; 125851433Smarkm } 125951433Smarkm } 126051433Smarkm 126151433Smarkm *resp = reply; 126251433Smarkm return PAM_SUCCESS; 126351433Smarkm} 126451433Smarkm 126551433Smarkm/* 126651433Smarkm * Attempt to authenticate the user using PAM. Returns 0 if the user is 126751433Smarkm * authenticated, or 1 if not authenticated. If some sort of PAM system 126851433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 126951433Smarkm * function returns -1. This can be used as an indication that we should 127051433Smarkm * fall back to a different authentication mechanism. 127151433Smarkm */ 127251433Smarkmstatic int 127351433Smarkmauth_pam(struct passwd **ppw, const char *pass) 127451433Smarkm{ 127551433Smarkm const char *tmpl_user; 127651433Smarkm const void *item; 127751433Smarkm int rval; 127851433Smarkm int e; 127951433Smarkm cred_t auth_cred = { (*ppw)->pw_name, pass }; 128051433Smarkm struct pam_conv conv = { &auth_conv, &auth_cred }; 128151433Smarkm 128251433Smarkm e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 128351433Smarkm if (e != PAM_SUCCESS) { 1284137108Syar /* 1285137108Syar * In OpenPAM, it's OK to pass NULL to pam_strerror() 1286137108Syar * if context creation has failed in the first place. 1287137108Syar */ 1288137108Syar syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); 128951433Smarkm return -1; 129051433Smarkm } 129151433Smarkm 129267007Sguido e = pam_set_item(pamh, PAM_RHOST, remotehost); 129367007Sguido if (e != PAM_SUCCESS) { 129467007Sguido syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 129567007Sguido pam_strerror(pamh, e)); 1296137078Syar if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 1297137078Syar syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 1298137078Syar } 1299137078Syar pamh = NULL; 130067007Sguido return -1; 130167007Sguido } 130267007Sguido 130351433Smarkm e = pam_authenticate(pamh, 0); 130451433Smarkm switch (e) { 130551433Smarkm case PAM_SUCCESS: 130651433Smarkm /* 130751433Smarkm * With PAM we support the concept of a "template" 130851433Smarkm * user. The user enters a login name which is 130951433Smarkm * authenticated by PAM, usually via a remote service 131051433Smarkm * such as RADIUS or TACACS+. If authentication 131151433Smarkm * succeeds, a different but related "template" name 131251433Smarkm * is used for setting the credentials, shell, and 131351433Smarkm * home directory. The name the user enters need only 131451433Smarkm * exist on the remote authentication server, but the 131551433Smarkm * template name must be present in the local password 131651433Smarkm * database. 131751433Smarkm * 131851433Smarkm * This is supported by two various mechanisms in the 131951433Smarkm * individual modules. However, from the application's 132051433Smarkm * point of view, the template user is always passed 132151433Smarkm * back as a changed value of the PAM_USER item. 132251433Smarkm */ 132351433Smarkm if ((e = pam_get_item(pamh, PAM_USER, &item)) == 132451433Smarkm PAM_SUCCESS) { 132551433Smarkm tmpl_user = (const char *) item; 132651433Smarkm if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 132751433Smarkm *ppw = getpwnam(tmpl_user); 132851433Smarkm } else 132951433Smarkm syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 133051433Smarkm pam_strerror(pamh, e)); 133151433Smarkm rval = 0; 133251433Smarkm break; 133351433Smarkm 133451433Smarkm case PAM_AUTH_ERR: 133551433Smarkm case PAM_USER_UNKNOWN: 133651433Smarkm case PAM_MAXTRIES: 133751433Smarkm rval = 1; 133851433Smarkm break; 133951433Smarkm 134051433Smarkm default: 134174874Smarkm syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 134251433Smarkm rval = -1; 134351433Smarkm break; 134451433Smarkm } 134551433Smarkm 134674874Smarkm if (rval == 0) { 134774874Smarkm e = pam_acct_mgmt(pamh, 0); 1348137986Syar if (e != PAM_SUCCESS) { 1349137986Syar syslog(LOG_ERR, "pam_acct_mgmt: %s", 1350137986Syar pam_strerror(pamh, e)); 135174874Smarkm rval = 1; 135274874Smarkm } 135351433Smarkm } 135474874Smarkm 135574874Smarkm if (rval != 0) { 135674874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 135774874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 135874874Smarkm } 135974874Smarkm pamh = NULL; 136074874Smarkm } 136151433Smarkm return rval; 136251433Smarkm} 136351433Smarkm 136474874Smarkm#endif /* USE_PAM */ 136551433Smarkm 13661592Srgrimesvoid 136790148Simppass(char *passwd) 13681592Srgrimes{ 1369216932Scsjp int rval, ecode; 13701592Srgrimes FILE *fd; 137125101Sdavidn#ifdef LOGIN_CAP 137225101Sdavidn login_cap_t *lc = NULL; 137325101Sdavidn#endif 137474874Smarkm#ifdef USE_PAM 137574874Smarkm int e; 137674874Smarkm#endif 1377109939Syar char *residue = NULL; 137888763Sache char *xpasswd; 13791592Srgrimes 13801592Srgrimes if (logged_in || askpasswd == 0) { 13811592Srgrimes reply(503, "Login with USER first."); 13821592Srgrimes return; 13831592Srgrimes } 13841592Srgrimes askpasswd = 0; 13851592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 138617435Spst if (pw == NULL) { 138717435Spst rval = 1; /* failure below */ 138817435Spst goto skip; 138917435Spst } 139074874Smarkm#ifdef USE_PAM 139151433Smarkm rval = auth_pam(&pw, passwd); 139289622Sache if (rval >= 0) { 139389622Sache opieunlock(); 139417435Spst goto skip; 139589622Sache } 139689622Sache#endif 139788763Sache if (opieverify(&opiedata, passwd) == 0) 139888763Sache xpasswd = pw->pw_passwd; 139989622Sache else if (pwok) { 140088763Sache xpasswd = crypt(passwd, pw->pw_passwd); 140189622Sache if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 140289622Sache xpasswd = ":"; 140389622Sache } else { 140488763Sache rval = 1; 140588763Sache goto skip; 140688763Sache } 140788763Sache rval = strcmp(pw->pw_passwd, xpasswd); 140889622Sache if (pw->pw_expire && time(NULL) >= pw->pw_expire) 140917435Spst rval = 1; /* failure */ 141017435Spstskip: 141117435Spst /* 141217435Spst * If rval == 1, the user failed the authentication check 141351433Smarkm * above. If rval == 0, either PAM or local authentication 141417435Spst * succeeded. 141517435Spst */ 141617435Spst if (rval) { 14171592Srgrimes reply(530, "Login incorrect."); 1418110691Syar if (logging) { 14191592Srgrimes syslog(LOG_NOTICE, 1420110691Syar "FTP LOGIN FAILED FROM %s", 1421110691Syar remotehost); 1422110691Syar syslog(LOG_AUTHPRIV | LOG_NOTICE, 14231592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 14241592Srgrimes remotehost, curname); 1425110691Syar } 14261592Srgrimes pw = NULL; 14271592Srgrimes if (login_attempts++ >= 5) { 14281592Srgrimes syslog(LOG_NOTICE, 14291592Srgrimes "repeated login failures from %s", 14301592Srgrimes remotehost); 14311592Srgrimes exit(0); 14321592Srgrimes } 14331592Srgrimes return; 14341592Srgrimes } 14351592Srgrimes } 14361592Srgrimes login_attempts = 0; /* this time successful */ 1437132894Syar if (setegid(pw->pw_gid) < 0) { 14381592Srgrimes reply(550, "Can't set gid."); 14391592Srgrimes return; 14401592Srgrimes } 144125101Sdavidn /* May be overridden by login.conf */ 144225101Sdavidn (void) umask(defumask); 144325101Sdavidn#ifdef LOGIN_CAP 144425674Sdavidn if ((lc = login_getpwclass(pw)) != NULL) { 1445137983Syar char remote_ip[NI_MAXHOST]; 144625101Sdavidn 1447137983Syar if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 144856668Sshin remote_ip, sizeof(remote_ip) - 1, NULL, 0, 1449137983Syar NI_NUMERICHOST)) 1450137983Syar *remote_ip = 0; 145125101Sdavidn remote_ip[sizeof(remote_ip) - 1] = 0; 145225101Sdavidn if (!auth_hostok(lc, remotehost, remote_ip)) { 145325101Sdavidn syslog(LOG_INFO|LOG_AUTH, 145425101Sdavidn "FTP LOGIN FAILED (HOST) as %s: permission denied.", 145525101Sdavidn pw->pw_name); 1456137849Syar reply(530, "Permission denied."); 145725101Sdavidn pw = NULL; 145825101Sdavidn return; 145925101Sdavidn } 146025101Sdavidn if (!auth_timeok(lc, time(NULL))) { 1461137849Syar reply(530, "Login not available right now."); 146225101Sdavidn pw = NULL; 146325101Sdavidn return; 146425101Sdavidn } 146525101Sdavidn } 1466223434Strasz setusercontext(lc, pw, 0, LOGIN_SETALL & 1467223434Strasz ~(LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV)); 146825101Sdavidn#else 146940310Sdes setlogin(pw->pw_name); 14701592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 147125101Sdavidn#endif 14721592Srgrimes 147374874Smarkm#ifdef USE_PAM 147474874Smarkm if (pamh) { 147574874Smarkm if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 147674874Smarkm syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 147774874Smarkm } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 147874874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 147974874Smarkm } 148074874Smarkm } 148174874Smarkm#endif 148274874Smarkm 1483202209Sed dochroot = 1484216932Scsjp checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode) 1485202209Sed#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 1486202209Sed || login_getcapbool(lc, "ftp-chroot", 0) 1487202209Sed#endif 1488202209Sed ; 1489216932Scsjp /* 1490216932Scsjp * It is possible that checkuser() failed to open the chroot file. 1491216932Scsjp * If this is the case, report that logins are un-available, since we 1492216932Scsjp * have no way of checking whether or not the user should be chrooted. 1493216932Scsjp * We ignore ENOENT since it is not required that this file be present. 1494216932Scsjp */ 1495216932Scsjp if (ecode != 0 && ecode != ENOENT) { 1496216932Scsjp reply(530, "Login not available right now."); 1497216932Scsjp return; 1498216932Scsjp } 1499202209Sed chrootdir = NULL; 1500202209Sed 1501202604Sed /* Disable wtmp logging when chrooting. */ 1502202604Sed if (dochroot || guest) 1503202604Sed dowtmp = 0; 1504202604Sed if (dowtmp) 1505202209Sed ftpd_logwtmp(wtmpid, pw->pw_name, 1506102311Syar (struct sockaddr *)&his_addr); 15071592Srgrimes logged_in = 1; 15081592Srgrimes 150917435Spst if (guest && stats && statfd < 0) 151025283Sdavidn#ifdef VIRTUAL_HOSTING 1511130428Sobrien statfd = open(thishost->statfile, O_WRONLY|O_APPEND); 151225283Sdavidn#else 1513130428Sobrien statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); 151425283Sdavidn#endif 1515130428Sobrien if (statfd < 0) 15166740Sguido stats = 0; 15176740Sguido 1518110036Syar /* 1519110036Syar * For a chrooted local user, 1520110036Syar * a) see whether ftpchroot(5) specifies a chroot directory, 1521110036Syar * b) extract the directory pathname from the line, 1522110036Syar * c) expand it to the absolute pathname if necessary. 1523110036Syar */ 1524110036Syar if (dochroot && residue && 1525117349Syar (chrootdir = strtok(residue, " \t")) != NULL) { 1526117349Syar if (chrootdir[0] != '/') 1527117349Syar asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); 1528117349Syar else 1529137862Syar chrootdir = strdup(chrootdir); /* make it permanent */ 1530110036Syar if (chrootdir == NULL) 1531110036Syar fatalerror("Ran out of memory."); 1532110036Syar } 1533110036Syar if (guest || dochroot) { 15341592Srgrimes /* 1535110036Syar * If no chroot directory set yet, use the login directory. 1536110036Syar * Copy it so it can be modified while pw->pw_dir stays intact. 15371592Srgrimes */ 1538110036Syar if (chrootdir == NULL && 1539110036Syar (chrootdir = strdup(pw->pw_dir)) == NULL) 1540110036Syar fatalerror("Ran out of memory."); 1541110036Syar /* 1542110036Syar * Check for the "/chroot/./home" syntax, 1543110036Syar * separate the chroot and home directory pathnames. 1544110036Syar */ 1545110036Syar if ((homedir = strstr(chrootdir, "/./")) != NULL) { 1546110036Syar *(homedir++) = '\0'; /* wipe '/' */ 1547110036Syar homedir++; /* skip '.' */ 1548110036Syar } else { 1549110036Syar /* 1550110036Syar * We MUST do a chdir() after the chroot. Otherwise 1551110036Syar * the old current directory will be accessible as "." 1552110036Syar * outside the new root! 1553110036Syar */ 1554110036Syar homedir = "/"; 1555109939Syar } 1556110036Syar /* 1557110036Syar * Finally, do chroot() 1558110036Syar */ 1559110036Syar if (chroot(chrootdir) < 0) { 156017435Spst reply(550, "Can't change root."); 156117435Spst goto bad; 156217435Spst } 1563228843Scperciva __FreeBSD_libc_enter_restricted_mode(); 1564110036Syar } else /* real user w/o chroot */ 1565110036Syar homedir = pw->pw_dir; 1566110036Syar /* 1567110036Syar * Set euid *before* doing chdir() so 1568110036Syar * a) the user won't be carried to a directory that he couldn't reach 1569110036Syar * on his own due to no permission to upper path components, 1570110036Syar * b) NFS mounted homedirs w/restrictive permissions will be accessible 1571110036Syar * (uid 0 has no root power over NFS if not mapped explicitly.) 1572110036Syar */ 1573132893Syar if (seteuid(pw->pw_uid) < 0) { 15741592Srgrimes reply(550, "Can't set uid."); 15751592Srgrimes goto bad; 15761592Srgrimes } 1577110036Syar if (chdir(homedir) < 0) { 1578110036Syar if (guest || dochroot) { 1579110036Syar reply(550, "Can't change to base directory."); 1580110036Syar goto bad; 1581110036Syar } else { 1582110036Syar if (chdir("/") < 0) { 1583110036Syar reply(550, "Root is inaccessible."); 1584110036Syar goto bad; 1585110036Syar } 1586137852Syar lreply(230, "No directory! Logging in with home=/."); 1587110036Syar } 1588110036Syar } 15898696Sdg 15901592Srgrimes /* 15911592Srgrimes * Display a login message, if it exists. 15921592Srgrimes * N.B. reply(230,) must follow the message. 15931592Srgrimes */ 159425283Sdavidn#ifdef VIRTUAL_HOSTING 1595130428Sobrien fd = fopen(thishost->loginmsg, "r"); 159625283Sdavidn#else 1597130428Sobrien fd = fopen(_PATH_FTPLOGINMESG, "r"); 159825283Sdavidn#endif 1599130428Sobrien if (fd != NULL) { 16001592Srgrimes char *cp, line[LINE_MAX]; 16011592Srgrimes 16021592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 16031592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 16041592Srgrimes *cp = '\0'; 16051592Srgrimes lreply(230, "%s", line); 16061592Srgrimes } 16071592Srgrimes (void) fflush(stdout); 16081592Srgrimes (void) fclose(fd); 16091592Srgrimes } 16101592Srgrimes if (guest) { 16116740Sguido if (ident != NULL) 16126740Sguido free(ident); 161317433Spst ident = strdup(passwd); 161417433Spst if (ident == NULL) 161576096Smarkm fatalerror("Ran out of memory."); 161617433Spst 16171592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 16181592Srgrimes#ifdef SETPROCTITLE 161925283Sdavidn#ifdef VIRTUAL_HOSTING 162025283Sdavidn if (thishost != firsthost) 162125283Sdavidn snprintf(proctitle, sizeof(proctitle), 162283308Smikeh "%s: anonymous(%s)/%s", remotehost, hostname, 162383308Smikeh passwd); 162425283Sdavidn else 162525283Sdavidn#endif 162625283Sdavidn snprintf(proctitle, sizeof(proctitle), 162783308Smikeh "%s: anonymous/%s", remotehost, passwd); 162813139Speter setproctitle("%s", proctitle); 16291592Srgrimes#endif /* SETPROCTITLE */ 16301592Srgrimes if (logging) 16311592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 16321592Srgrimes remotehost, passwd); 16331592Srgrimes } else { 163484841Syar if (dochroot) 163584841Syar reply(230, "User %s logged in, " 163684841Syar "access restrictions apply.", pw->pw_name); 163784841Syar else 163884841Syar reply(230, "User %s logged in.", pw->pw_name); 163925986Sdanny 16401592Srgrimes#ifdef SETPROCTITLE 16411592Srgrimes snprintf(proctitle, sizeof(proctitle), 164284842Syar "%s: user/%s", remotehost, pw->pw_name); 164313139Speter setproctitle("%s", proctitle); 16441592Srgrimes#endif /* SETPROCTITLE */ 16451592Srgrimes if (logging) 16461592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 16471592Srgrimes remotehost, pw->pw_name); 16481592Srgrimes } 1649140473Syar if (logging && (guest || dochroot)) 1650137985Syar syslog(LOG_INFO, "session root changed to %s", chrootdir); 165125101Sdavidn#ifdef LOGIN_CAP 165225101Sdavidn login_close(lc); 165325101Sdavidn#endif 1654110036Syar if (residue) 1655110036Syar free(residue); 16561592Srgrimes return; 16571592Srgrimesbad: 16581592Srgrimes /* Forget all about it... */ 165925101Sdavidn#ifdef LOGIN_CAP 166025101Sdavidn login_close(lc); 166125101Sdavidn#endif 1662110036Syar if (residue) 1663110036Syar free(residue); 16641592Srgrimes end_login(); 16651592Srgrimes} 16661592Srgrimes 16671592Srgrimesvoid 166890148Simpretrieve(char *cmd, char *name) 16691592Srgrimes{ 16701592Srgrimes FILE *fin, *dout; 16711592Srgrimes struct stat st; 167290148Simp int (*closefunc)(FILE *); 167336612Sjb time_t start; 16741592Srgrimes 16751592Srgrimes if (cmd == 0) { 16761592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 16771592Srgrimes st.st_size = 0; 16781592Srgrimes } else { 16791592Srgrimes char line[BUFSIZ]; 16801592Srgrimes 168131973Simp (void) snprintf(line, sizeof(line), cmd, name), name = line; 16821592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 16831592Srgrimes st.st_size = -1; 16841592Srgrimes st.st_blksize = BUFSIZ; 16851592Srgrimes } 16861592Srgrimes if (fin == NULL) { 16871592Srgrimes if (errno != 0) { 16881592Srgrimes perror_reply(550, name); 16891592Srgrimes if (cmd == 0) { 16901592Srgrimes LOGCMD("get", name); 16911592Srgrimes } 16921592Srgrimes } 16931592Srgrimes return; 16941592Srgrimes } 16951592Srgrimes byte_count = -1; 1696110144Syar if (cmd == 0) { 1697110144Syar if (fstat(fileno(fin), &st) < 0) { 1698110144Syar perror_reply(550, name); 1699110144Syar goto done; 1700110144Syar } 1701110144Syar if (!S_ISREG(st.st_mode)) { 1702125565Syar /* 1703125565Syar * Never sending a raw directory is a workaround 1704125565Syar * for buggy clients that will attempt to RETR 1705125565Syar * a directory before listing it, e.g., Mozilla. 1706125565Syar * Preventing a guest from getting irregular files 1707125565Syar * is a simple security measure. 1708125565Syar */ 1709125565Syar if (S_ISDIR(st.st_mode) || guest) { 1710110144Syar reply(550, "%s: not a plain file.", name); 1711110144Syar goto done; 1712110144Syar } 1713110144Syar st.st_size = -1; 1714110144Syar /* st.st_blksize is set for all descriptor types */ 1715110144Syar } 17161592Srgrimes } 17171592Srgrimes if (restart_point) { 17181592Srgrimes if (type == TYPE_A) { 17191592Srgrimes off_t i, n; 17201592Srgrimes int c; 17211592Srgrimes 17221592Srgrimes n = restart_point; 17231592Srgrimes i = 0; 17241592Srgrimes while (i++ < n) { 17251592Srgrimes if ((c=getc(fin)) == EOF) { 17261592Srgrimes perror_reply(550, name); 17271592Srgrimes goto done; 17281592Srgrimes } 17291592Srgrimes if (c == '\n') 17301592Srgrimes i++; 17311592Srgrimes } 17321592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 17331592Srgrimes perror_reply(550, name); 17341592Srgrimes goto done; 17351592Srgrimes } 17361592Srgrimes } 17371592Srgrimes dout = dataconn(name, st.st_size, "w"); 17381592Srgrimes if (dout == NULL) 17391592Srgrimes goto done; 17406740Sguido time(&start); 17418240Swollman send_data(fin, dout, st.st_blksize, st.st_size, 17428240Swollman restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 1743136929Syar if (cmd == 0 && guest && stats && byte_count > 0) 1744136929Syar logxfer(name, byte_count, start); 17451592Srgrimes (void) fclose(dout); 17461592Srgrimes data = -1; 17471592Srgrimes pdata = -1; 17481592Srgrimesdone: 17491592Srgrimes if (cmd == 0) 17501592Srgrimes LOGBYTES("get", name, byte_count); 17511592Srgrimes (*closefunc)(fin); 17521592Srgrimes} 17531592Srgrimes 17541592Srgrimesvoid 175590148Simpstore(char *name, char *mode, int unique) 17561592Srgrimes{ 1757101537Syar int fd; 17581592Srgrimes FILE *fout, *din; 175990148Simp int (*closefunc)(FILE *); 17601592Srgrimes 1761101537Syar if (*mode == 'a') { /* APPE */ 1762101537Syar if (unique) { 1763101537Syar /* Programming error */ 1764101537Syar syslog(LOG_ERR, "Internal: unique flag to APPE"); 1765101537Syar unique = 0; 1766101537Syar } 1767101537Syar if (guest && noguestmod) { 1768137852Syar reply(550, "Appending to existing file denied."); 1769101537Syar goto err; 1770101537Syar } 1771101537Syar restart_point = 0; /* not affected by preceding REST */ 17721592Srgrimes } 1773101537Syar if (unique) /* STOU overrides REST */ 1774101537Syar restart_point = 0; 1775101537Syar if (guest && noguestmod) { 1776101537Syar if (restart_point) { /* guest STOR w/REST */ 1777137852Syar reply(550, "Modifying existing file denied."); 1778101537Syar goto err; 1779101537Syar } else /* treat guest STOR as STOU */ 1780101537Syar unique = 1; 1781101537Syar } 17821592Srgrimes 17831592Srgrimes if (restart_point) 1784101537Syar mode = "r+"; /* so ASCII manual seek can work */ 1785101537Syar if (unique) { 1786101537Syar if ((fd = guniquefd(name, &name)) < 0) 1787101537Syar goto err; 1788101537Syar fout = fdopen(fd, mode); 1789101537Syar } else 1790101537Syar fout = fopen(name, mode); 17911592Srgrimes closefunc = fclose; 17921592Srgrimes if (fout == NULL) { 17931592Srgrimes perror_reply(553, name); 1794101537Syar goto err; 17951592Srgrimes } 17961592Srgrimes byte_count = -1; 17971592Srgrimes if (restart_point) { 17981592Srgrimes if (type == TYPE_A) { 17991592Srgrimes off_t i, n; 18001592Srgrimes int c; 18011592Srgrimes 18021592Srgrimes n = restart_point; 18031592Srgrimes i = 0; 18041592Srgrimes while (i++ < n) { 18051592Srgrimes if ((c=getc(fout)) == EOF) { 18061592Srgrimes perror_reply(550, name); 18071592Srgrimes goto done; 18081592Srgrimes } 18091592Srgrimes if (c == '\n') 18101592Srgrimes i++; 18111592Srgrimes } 18121592Srgrimes /* 18131592Srgrimes * We must do this seek to "current" position 18141592Srgrimes * because we are changing from reading to 18151592Srgrimes * writing. 18161592Srgrimes */ 1817132930Syar if (fseeko(fout, 0, SEEK_CUR) < 0) { 18181592Srgrimes perror_reply(550, name); 18191592Srgrimes goto done; 18201592Srgrimes } 18211592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 18221592Srgrimes perror_reply(550, name); 18231592Srgrimes goto done; 18241592Srgrimes } 18251592Srgrimes } 1826132930Syar din = dataconn(name, -1, "r"); 18271592Srgrimes if (din == NULL) 18281592Srgrimes goto done; 18291592Srgrimes if (receive_data(din, fout) == 0) { 18301592Srgrimes if (unique) 18311592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 18321592Srgrimes name); 18331592Srgrimes else 18341592Srgrimes reply(226, "Transfer complete."); 18351592Srgrimes } 18361592Srgrimes (void) fclose(din); 18371592Srgrimes data = -1; 18381592Srgrimes pdata = -1; 18391592Srgrimesdone: 1840102566Syar LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); 18411592Srgrimes (*closefunc)(fout); 1842101537Syar return; 1843101537Syarerr: 1844101537Syar LOGCMD(*mode == 'a' ? "append" : "put" , name); 1845101537Syar return; 18461592Srgrimes} 18471592Srgrimes 18481592Srgrimesstatic FILE * 184990148Simpgetdatasock(char *mode) 18501592Srgrimes{ 18511592Srgrimes int on = 1, s, t, tries; 18521592Srgrimes 18531592Srgrimes if (data >= 0) 18541592Srgrimes return (fdopen(data, mode)); 185556668Sshin 185656668Sshin s = socket(data_dest.su_family, SOCK_STREAM, 0); 18571592Srgrimes if (s < 0) 18581592Srgrimes goto bad; 1859100612Syar if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1860100609Syar syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 18611592Srgrimes /* anchor socket to avoid multi-homing problems */ 186256668Sshin data_source = ctrl_addr; 1863109742Syar data_source.su_port = htons(dataport); 1864132893Syar (void) seteuid(0); 18651592Srgrimes for (tries = 1; ; tries++) { 1866132891Syar /* 1867132891Syar * We should loop here since it's possible that 1868132891Syar * another ftpd instance has passed this point and is 1869132891Syar * trying to open a data connection in active mode now. 1870132891Syar * Until the other connection is opened, we'll be getting 1871132891Syar * EADDRINUSE because no SOCK_STREAM sockets in the system 1872132891Syar * can share both local and remote addresses, localIP:20 1873132891Syar * and *:* in this case. 1874132891Syar */ 18751592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 187656668Sshin data_source.su_len) >= 0) 18771592Srgrimes break; 18781592Srgrimes if (errno != EADDRINUSE || tries > 10) 18791592Srgrimes goto bad; 18801592Srgrimes sleep(tries); 18811592Srgrimes } 1882132893Syar (void) seteuid(pw->pw_uid); 18831592Srgrimes#ifdef IP_TOS 188456668Sshin if (data_source.su_family == AF_INET) 188556668Sshin { 18861592Srgrimes on = IPTOS_THROUGHPUT; 1887100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 1888100609Syar syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 188956668Sshin } 18901592Srgrimes#endif 18918240Swollman#ifdef TCP_NOPUSH 18928240Swollman /* 18938240Swollman * Turn off push flag to keep sender TCP from sending short packets 1894166598Syar * at the boundaries of each write(). 18958240Swollman */ 18968240Swollman on = 1; 1897100612Syar if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 1898100609Syar syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 18998240Swollman#endif 19001592Srgrimes return (fdopen(s, mode)); 19011592Srgrimesbad: 19021592Srgrimes /* Return the real value of errno (close may change it) */ 19031592Srgrimes t = errno; 1904132893Syar (void) seteuid(pw->pw_uid); 19051592Srgrimes (void) close(s); 19061592Srgrimes errno = t; 19071592Srgrimes return (NULL); 19081592Srgrimes} 19091592Srgrimes 19101592Srgrimesstatic FILE * 191190148Simpdataconn(char *name, off_t size, char *mode) 19121592Srgrimes{ 19131592Srgrimes char sizebuf[32]; 19141592Srgrimes FILE *file; 1915109611Scjc int retry = 0, tos, conerrno; 19161592Srgrimes 19171592Srgrimes file_size = size; 19181592Srgrimes byte_count = 0; 1919132930Syar if (size != -1) 1920132929Syar (void) snprintf(sizebuf, sizeof(sizebuf), 1921132929Syar " (%jd bytes)", (intmax_t)size); 19221592Srgrimes else 192331973Simp *sizebuf = '\0'; 19241592Srgrimes if (pdata >= 0) { 192556668Sshin union sockunion from; 1926141918Sstefanf socklen_t fromlen = ctrl_addr.su_len; 1927141918Sstefanf int flags, s; 192812532Sguido struct timeval timeout; 192912532Sguido fd_set set; 19301592Srgrimes 193112532Sguido FD_ZERO(&set); 193212532Sguido FD_SET(pdata, &set); 193312532Sguido 193412532Sguido timeout.tv_usec = 0; 193512532Sguido timeout.tv_sec = 120; 193612532Sguido 193786628Syar /* 193886628Syar * Granted a socket is in the blocking I/O mode, 193986628Syar * accept() will block after a successful select() 194086628Syar * if the selected connection dies in between. 194186628Syar * Therefore set the non-blocking I/O flag here. 194286628Syar */ 194386628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 194486628Syar fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 194586628Syar goto pdata_err; 1946132931Syar if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || 194786628Syar (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 194886628Syar goto pdata_err; 19491592Srgrimes (void) close(pdata); 19501592Srgrimes pdata = s; 195186628Syar /* 1952101809Syar * Unset the inherited non-blocking I/O flag 1953101809Syar * on the child socket so stdio can work on it. 195486628Syar */ 195586628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 195686628Syar fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 195786628Syar goto pdata_err; 19581592Srgrimes#ifdef IP_TOS 195956668Sshin if (from.su_family == AF_INET) 196056668Sshin { 196117435Spst tos = IPTOS_THROUGHPUT; 1962100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 1963100609Syar syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 196456668Sshin } 19651592Srgrimes#endif 19661592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19671592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19681592Srgrimes return (fdopen(pdata, mode)); 196986628Syarpdata_err: 197086628Syar reply(425, "Can't open data connection."); 197186628Syar (void) close(pdata); 197286628Syar pdata = -1; 197386628Syar return (NULL); 19741592Srgrimes } 19751592Srgrimes if (data >= 0) { 19761592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 19771592Srgrimes name, sizebuf); 19781592Srgrimes usedefault = 1; 19791592Srgrimes return (fdopen(data, mode)); 19801592Srgrimes } 19811592Srgrimes if (usedefault) 19821592Srgrimes data_dest = his_addr; 19831592Srgrimes usedefault = 1; 1984109611Scjc do { 1985109611Scjc file = getdatasock(mode); 1986109611Scjc if (file == NULL) { 1987137983Syar char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 1988137983Syar 1989137983Syar if (getnameinfo((struct sockaddr *)&data_source, 1990137983Syar data_source.su_len, 1991137983Syar hostbuf, sizeof(hostbuf) - 1, 1992137983Syar portbuf, sizeof(portbuf) - 1, 1993137983Syar NI_NUMERICHOST|NI_NUMERICSERV)) 1994137983Syar *hostbuf = *portbuf = 0; 1995137983Syar hostbuf[sizeof(hostbuf) - 1] = 0; 1996137983Syar portbuf[sizeof(portbuf) - 1] = 0; 1997109611Scjc reply(425, "Can't create data socket (%s,%s): %s.", 1998109611Scjc hostbuf, portbuf, strerror(errno)); 1999109611Scjc return (NULL); 2000109611Scjc } 2001109611Scjc data = fileno(file); 2002109611Scjc conerrno = 0; 2003109611Scjc if (connect(data, (struct sockaddr *)&data_dest, 2004109611Scjc data_dest.su_len) == 0) 2005109611Scjc break; 2006109611Scjc conerrno = errno; 2007109611Scjc (void) fclose(file); 2008109611Scjc data = -1; 2009109611Scjc if (conerrno == EADDRINUSE) { 2010137659Syar sleep(swaitint); 20111592Srgrimes retry += swaitint; 2012109611Scjc } else { 2013109611Scjc break; 20141592Srgrimes } 2015109611Scjc } while (retry <= swaitmax); 2016109611Scjc if (conerrno != 0) { 2017137850Syar reply(425, "Can't build data connection: %s.", 2018137850Syar strerror(conerrno)); 20191592Srgrimes return (NULL); 20201592Srgrimes } 20211592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 20221592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 20231592Srgrimes return (file); 20241592Srgrimes} 20251592Srgrimes 20261592Srgrimes/* 2027140472Syar * A helper macro to avoid code duplication 2028140472Syar * in send_data() and receive_data(). 2029140472Syar * 2030140472Syar * XXX We have to block SIGURG during putc() because BSD stdio 2031140472Syar * is unable to restart interrupted write operations and hence 2032140472Syar * the entire buffer contents will be lost as soon as a write() 2033140472Syar * call indicates EINTR to stdio. 2034140472Syar */ 2035140472Syar#define FTPD_PUTC(ch, file, label) \ 2036140472Syar do { \ 2037140472Syar int ret; \ 2038140472Syar \ 2039140472Syar do { \ 2040140472Syar START_UNSAFE; \ 2041140472Syar ret = putc((ch), (file)); \ 2042140472Syar END_UNSAFE; \ 2043140472Syar CHECKOOB(return (-1)) \ 2044140472Syar else if (ferror(file)) \ 2045140472Syar goto label; \ 2046140472Syar clearerr(file); \ 2047140472Syar } while (ret == EOF); \ 2048140472Syar } while (0) 2049140472Syar 2050140472Syar/* 20511592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate 20528240Swollman * encapsulation of the data subject to Mode, Structure, and Type. 20531592Srgrimes * 20541592Srgrimes * NB: Form isn't handled. 20551592Srgrimes */ 205689935Syarstatic int 2057137660Syarsend_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg) 20581592Srgrimes{ 2059122751Syar int c, cp, filefd, netfd; 206070205Sdan char *buf; 20611592Srgrimes 2062140472Syar STARTXFER; 2063140472Syar 20641592Srgrimes switch (type) { 20651592Srgrimes 20661592Srgrimes case TYPE_A: 2067140472Syar cp = EOF; 2068140472Syar for (;;) { 2069140472Syar c = getc(instr); 2070140472Syar CHECKOOB(return (-1)) 2071140472Syar else if (c == EOF && ferror(instr)) 2072140472Syar goto file_err; 2073140472Syar if (c == EOF) { 2074140472Syar if (ferror(instr)) { /* resume after OOB */ 2075140472Syar clearerr(instr); 2076140472Syar continue; 2077140472Syar } 2078140472Syar if (feof(instr)) /* EOF */ 2079140472Syar break; 2080140472Syar syslog(LOG_ERR, "Internal: impossible condition" 2081140472Syar " on file after getc()"); 2082140472Syar goto file_err; 2083140472Syar } 2084122751Syar if (c == '\n' && cp != '\r') { 2085140472Syar FTPD_PUTC('\r', outstr, data_err); 2086140472Syar byte_count++; 20871592Srgrimes } 2088140472Syar FTPD_PUTC(c, outstr, data_err); 2089140472Syar byte_count++; 2090122751Syar cp = c; 20911592Srgrimes } 2092140472Syar#ifdef notyet /* BSD stdio isn't ready for that */ 2093140472Syar while (fflush(outstr) == EOF) { 2094140472Syar CHECKOOB(return (-1)) 2095140472Syar else 2096140472Syar goto data_err; 2097140472Syar clearerr(outstr); 2098140472Syar } 2099140472Syar ENDXFER; 2100140472Syar#else 2101140472Syar ENDXFER; 2102140472Syar if (fflush(outstr) == EOF) 21031592Srgrimes goto data_err; 2104140472Syar#endif 21051592Srgrimes reply(226, "Transfer complete."); 210689935Syar return (0); 21071592Srgrimes 21081592Srgrimes case TYPE_I: 21091592Srgrimes case TYPE_L: 21108240Swollman /* 21118240Swollman * isreg is only set if we are not doing restart and we 21128240Swollman * are sending a regular file 21138240Swollman */ 21148240Swollman netfd = fileno(outstr); 21158870Srgrimes filefd = fileno(instr); 21168240Swollman 211770205Sdan if (isreg) { 2118136554Syar char *msg = "Transfer complete."; 2119140472Syar off_t cnt, offset; 212070205Sdan int err; 212170205Sdan 2122136555Syar cnt = offset = 0; 212370205Sdan 2124136555Syar while (filesize > 0) { 212590604Smaxim err = sendfile(filefd, netfd, offset, 0, 2126137811Syar NULL, &cnt, 0); 212799212Smaxim /* 212899212Smaxim * Calculate byte_count before OOB processing. 212999212Smaxim * It can be used in myoob() later. 213099212Smaxim */ 213199212Smaxim byte_count += cnt; 213270205Sdan offset += cnt; 213390604Smaxim filesize -= cnt; 2134140472Syar CHECKOOB(return (-1)) 2135140472Syar else if (err == -1) { 2136140472Syar if (errno != EINTR && 2137140472Syar cnt == 0 && offset == 0) 213870205Sdan goto oldway; 213970205Sdan goto data_err; 214070205Sdan } 2141140472Syar if (err == -1) /* resume after OOB */ 2142140472Syar continue; 2143136554Syar /* 2144136554Syar * We hit the EOF prematurely. 2145136554Syar * Perhaps the file was externally truncated. 2146136554Syar */ 2147136554Syar if (cnt == 0) { 2148136554Syar msg = "Transfer finished due to " 2149136554Syar "premature end of file."; 2150136554Syar break; 2151136554Syar } 215270205Sdan } 2153140472Syar ENDXFER; 2154216945Semaste reply(226, "%s", msg); 215589935Syar return (0); 21568240Swollman } 21578240Swollman 21588240Swollmanoldway: 2159137659Syar if ((buf = malloc(blksize)) == NULL) { 2160140472Syar ENDXFER; 2161137852Syar reply(451, "Ran out of memory."); 216289935Syar return (-1); 21631592Srgrimes } 21648870Srgrimes 2165140472Syar for (;;) { 2166140472Syar int cnt, len; 2167140472Syar char *bp; 2168140472Syar 2169140472Syar cnt = read(filefd, buf, blksize); 2170140472Syar CHECKOOB(free(buf); return (-1)) 2171140472Syar else if (cnt < 0) { 2172140472Syar free(buf); 21731592Srgrimes goto file_err; 2174140472Syar } 2175140472Syar if (cnt < 0) /* resume after OOB */ 2176140472Syar continue; 2177140472Syar if (cnt == 0) /* EOF */ 2178140472Syar break; 2179140472Syar for (len = cnt, bp = buf; len > 0;) { 2180140472Syar cnt = write(netfd, bp, len); 2181140472Syar CHECKOOB(free(buf); return (-1)) 2182140472Syar else if (cnt < 0) { 2183140472Syar free(buf); 2184140472Syar goto data_err; 2185140472Syar } 2186140472Syar if (cnt <= 0) 2187140472Syar continue; 2188140472Syar len -= cnt; 2189140472Syar bp += cnt; 2190140472Syar byte_count += cnt; 2191140472Syar } 21921592Srgrimes } 2193140472Syar ENDXFER; 2194140472Syar free(buf); 21951592Srgrimes reply(226, "Transfer complete."); 219689935Syar return (0); 21971592Srgrimes default: 2198140472Syar ENDXFER; 2199137852Syar reply(550, "Unimplemented TYPE %d in send_data.", type); 220089935Syar return (-1); 22011592Srgrimes } 22021592Srgrimes 22031592Srgrimesdata_err: 2204140472Syar ENDXFER; 22051592Srgrimes perror_reply(426, "Data connection"); 220689935Syar return (-1); 22071592Srgrimes 22081592Srgrimesfile_err: 2209140472Syar ENDXFER; 22101592Srgrimes perror_reply(551, "Error on input file"); 221189935Syar return (-1); 22121592Srgrimes} 22131592Srgrimes 22141592Srgrimes/* 22151592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 22161592Srgrimes * the data subject to Mode, Structure, and Type. 22171592Srgrimes * 22181592Srgrimes * N.B.: Form isn't handled. 22191592Srgrimes */ 22201592Srgrimesstatic int 222190148Simpreceive_data(FILE *instr, FILE *outstr) 22221592Srgrimes{ 2223140472Syar int c, cp; 2224140472Syar int bare_lfs = 0; 22251592Srgrimes 2226140472Syar STARTXFER; 222717433Spst 22281592Srgrimes switch (type) { 22291592Srgrimes 22301592Srgrimes case TYPE_I: 22311592Srgrimes case TYPE_L: 2232140472Syar for (;;) { 2233140472Syar int cnt, len; 2234140472Syar char *bp; 2235140472Syar char buf[BUFSIZ]; 2236140472Syar 2237140472Syar cnt = read(fileno(instr), buf, sizeof(buf)); 2238140472Syar CHECKOOB(return (-1)) 2239140472Syar else if (cnt < 0) 2240140472Syar goto data_err; 2241140472Syar if (cnt < 0) /* resume after OOB */ 2242140472Syar continue; 2243140472Syar if (cnt == 0) /* EOF */ 2244140472Syar break; 2245140472Syar for (len = cnt, bp = buf; len > 0;) { 2246140472Syar cnt = write(fileno(outstr), bp, len); 2247140472Syar CHECKOOB(return (-1)) 2248140472Syar else if (cnt < 0) 2249140472Syar goto file_err; 2250140472Syar if (cnt <= 0) 2251140472Syar continue; 2252140472Syar len -= cnt; 2253140472Syar bp += cnt; 2254140472Syar byte_count += cnt; 2255140472Syar } 22561592Srgrimes } 2257140472Syar ENDXFER; 22581592Srgrimes return (0); 22591592Srgrimes 22601592Srgrimes case TYPE_E: 2261140472Syar ENDXFER; 22621592Srgrimes reply(553, "TYPE E not implemented."); 22631592Srgrimes return (-1); 22641592Srgrimes 22651592Srgrimes case TYPE_A: 2266140472Syar cp = EOF; 2267140472Syar for (;;) { 2268140472Syar c = getc(instr); 2269140472Syar CHECKOOB(return (-1)) 2270140472Syar else if (c == EOF && ferror(instr)) 2271140472Syar goto data_err; 2272140472Syar if (c == EOF && ferror(instr)) { /* resume after OOB */ 2273140472Syar clearerr(instr); 2274140472Syar continue; 2275140472Syar } 2276140472Syar 2277140472Syar if (cp == '\r') { 2278140472Syar if (c != '\n') 2279140472Syar FTPD_PUTC('\r', outstr, file_err); 2280140472Syar } else 2281140472Syar if (c == '\n') 2282140472Syar bare_lfs++; 2283140472Syar if (c == '\r') { 2284140472Syar byte_count++; 2285140472Syar cp = c; 2286140472Syar continue; 2287140472Syar } 2288140472Syar 2289140472Syar /* Check for EOF here in order not to lose last \r. */ 2290140472Syar if (c == EOF) { 2291140472Syar if (feof(instr)) /* EOF */ 2292140472Syar break; 2293140472Syar syslog(LOG_ERR, "Internal: impossible condition" 2294140472Syar " on data stream after getc()"); 2295140472Syar goto data_err; 2296140472Syar } 2297140472Syar 22981592Srgrimes byte_count++; 2299140472Syar FTPD_PUTC(c, outstr, file_err); 2300140472Syar cp = c; 23011592Srgrimes } 2302140472Syar#ifdef notyet /* BSD stdio isn't ready for that */ 2303140472Syar while (fflush(outstr) == EOF) { 2304140472Syar CHECKOOB(return (-1)) 2305140472Syar else 2306140472Syar goto file_err; 2307140472Syar clearerr(outstr); 2308140472Syar } 2309140472Syar ENDXFER; 2310140472Syar#else 2311140472Syar ENDXFER; 2312140472Syar if (fflush(outstr) == EOF) 23131592Srgrimes goto file_err; 2314140472Syar#endif 23151592Srgrimes if (bare_lfs) { 23161592Srgrimes lreply(226, 2317137852Syar "WARNING! %d bare linefeeds received in ASCII mode.", 23181592Srgrimes bare_lfs); 23191592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 23201592Srgrimes } 23211592Srgrimes return (0); 23221592Srgrimes default: 2323140472Syar ENDXFER; 2324137852Syar reply(550, "Unimplemented TYPE %d in receive_data.", type); 23251592Srgrimes return (-1); 23261592Srgrimes } 23271592Srgrimes 23281592Srgrimesdata_err: 2329140472Syar ENDXFER; 2330137852Syar perror_reply(426, "Data connection"); 23311592Srgrimes return (-1); 23321592Srgrimes 23331592Srgrimesfile_err: 2334140472Syar ENDXFER; 2335137852Syar perror_reply(452, "Error writing to file"); 23361592Srgrimes return (-1); 23371592Srgrimes} 23381592Srgrimes 23391592Srgrimesvoid 234090148Simpstatfilecmd(char *filename) 23411592Srgrimes{ 23421592Srgrimes FILE *fin; 2343109382Syar int atstart; 2344137728Syar int c, code; 23451592Srgrimes char line[LINE_MAX]; 2346137728Syar struct stat st; 23471592Srgrimes 2348137728Syar code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; 234925165Sdavidn (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 23501592Srgrimes fin = ftpd_popen(line, "r"); 2351216943Semaste if (fin == NULL) { 2352216943Semaste perror_reply(551, filename); 2353216943Semaste return; 2354216943Semaste } 2355137729Syar lreply(code, "Status of %s:", filename); 2356109382Syar atstart = 1; 23571592Srgrimes while ((c = getc(fin)) != EOF) { 23581592Srgrimes if (c == '\n') { 23591592Srgrimes if (ferror(stdout)){ 2360137852Syar perror_reply(421, "Control connection"); 23611592Srgrimes (void) ftpd_pclose(fin); 23621592Srgrimes dologout(1); 23631592Srgrimes /* NOTREACHED */ 23641592Srgrimes } 23651592Srgrimes if (ferror(fin)) { 23661592Srgrimes perror_reply(551, filename); 23671592Srgrimes (void) ftpd_pclose(fin); 23681592Srgrimes return; 23691592Srgrimes } 23701592Srgrimes (void) putc('\r', stdout); 23711592Srgrimes } 2372109382Syar /* 2373109382Syar * RFC 959 says neutral text should be prepended before 2374109382Syar * a leading 3-digit number followed by whitespace, but 2375109382Syar * many ftp clients can be confused by any leading digits, 2376109382Syar * as a matter of fact. 2377109382Syar */ 2378109382Syar if (atstart && isdigit(c)) 2379109382Syar (void) putc(' ', stdout); 23801592Srgrimes (void) putc(c, stdout); 2381109382Syar atstart = (c == '\n'); 23821592Srgrimes } 23831592Srgrimes (void) ftpd_pclose(fin); 2384137852Syar reply(code, "End of status."); 23851592Srgrimes} 23861592Srgrimes 23871592Srgrimesvoid 238890148Simpstatcmd(void) 23891592Srgrimes{ 239056668Sshin union sockunion *su; 23911592Srgrimes u_char *a, *p; 239299255Sume char hname[NI_MAXHOST]; 239356668Sshin int ispassive; 23941592Srgrimes 2395110037Syar if (hostinfo) { 2396110037Syar lreply(211, "%s FTP server status:", hostname); 2397110037Syar printf(" %s\r\n", version); 2398110037Syar } else 2399110037Syar lreply(211, "FTP server status:"); 24001592Srgrimes printf(" Connected to %s", remotehost); 240156668Sshin if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 240299255Sume hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 2403137983Syar hname[sizeof(hname) - 1] = 0; 240456668Sshin if (strcmp(hname, remotehost) != 0) 240556668Sshin printf(" (%s)", hname); 240656668Sshin } 24071592Srgrimes printf("\r\n"); 24081592Srgrimes if (logged_in) { 24091592Srgrimes if (guest) 24101592Srgrimes printf(" Logged in anonymously\r\n"); 24111592Srgrimes else 24121592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 24131592Srgrimes } else if (askpasswd) 24141592Srgrimes printf(" Waiting for password\r\n"); 24151592Srgrimes else 24161592Srgrimes printf(" Waiting for user name\r\n"); 24171592Srgrimes printf(" TYPE: %s", typenames[type]); 24181592Srgrimes if (type == TYPE_A || type == TYPE_E) 24191592Srgrimes printf(", FORM: %s", formnames[form]); 24201592Srgrimes if (type == TYPE_L) 2421103949Smike#if CHAR_BIT == 8 2422103949Smike printf(" %d", CHAR_BIT); 24231592Srgrimes#else 24241592Srgrimes printf(" %d", bytesize); /* need definition! */ 24251592Srgrimes#endif 24261592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 24271592Srgrimes strunames[stru], modenames[mode]); 24281592Srgrimes if (data != -1) 24291592Srgrimes printf(" Data connection open\r\n"); 24301592Srgrimes else if (pdata != -1) { 243156668Sshin ispassive = 1; 243256668Sshin su = &pasv_addr; 24331592Srgrimes goto printaddr; 24341592Srgrimes } else if (usedefault == 0) { 243556668Sshin ispassive = 0; 243656668Sshin su = &data_dest; 24371592Srgrimesprintaddr: 24381592Srgrimes#define UC(b) (((int) b) & 0xff) 243956668Sshin if (epsvall) { 244056668Sshin printf(" EPSV only mode (EPSV ALL)\r\n"); 244156668Sshin goto epsvonly; 244256668Sshin } 244356668Sshin 244456668Sshin /* PORT/PASV */ 244556668Sshin if (su->su_family == AF_INET) { 244656668Sshin a = (u_char *) &su->su_sin.sin_addr; 244756668Sshin p = (u_char *) &su->su_sin.sin_port; 244856668Sshin printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 244956668Sshin ispassive ? "PASV" : "PORT", 245056668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 245156668Sshin UC(p[0]), UC(p[1])); 245256668Sshin } 245356668Sshin 245456668Sshin /* LPRT/LPSV */ 245556668Sshin { 245656668Sshin int alen, af, i; 245756668Sshin 245856668Sshin switch (su->su_family) { 245956668Sshin case AF_INET: 246056668Sshin a = (u_char *) &su->su_sin.sin_addr; 246156668Sshin p = (u_char *) &su->su_sin.sin_port; 246256668Sshin alen = sizeof(su->su_sin.sin_addr); 246356668Sshin af = 4; 246456668Sshin break; 246556668Sshin case AF_INET6: 246656668Sshin a = (u_char *) &su->su_sin6.sin6_addr; 246756668Sshin p = (u_char *) &su->su_sin6.sin6_port; 246856668Sshin alen = sizeof(su->su_sin6.sin6_addr); 246956668Sshin af = 6; 247056668Sshin break; 247156668Sshin default: 247256668Sshin af = 0; 247356668Sshin break; 247456668Sshin } 247556668Sshin if (af) { 247656668Sshin printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 247756668Sshin af, alen); 247856668Sshin for (i = 0; i < alen; i++) 247956668Sshin printf("%d,", UC(a[i])); 248056668Sshin printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 248156668Sshin } 248256668Sshin } 248356668Sshin 248456668Sshinepsvonly:; 248556668Sshin /* EPRT/EPSV */ 248656668Sshin { 248756668Sshin int af; 248856668Sshin 248956668Sshin switch (su->su_family) { 249056668Sshin case AF_INET: 249156668Sshin af = 1; 249256668Sshin break; 249356668Sshin case AF_INET6: 249456668Sshin af = 2; 249556668Sshin break; 249656668Sshin default: 249756668Sshin af = 0; 249856668Sshin break; 249956668Sshin } 250056668Sshin if (af) { 250199255Sume union sockunion tmp; 250299255Sume 250399255Sume tmp = *su; 250499255Sume if (tmp.su_family == AF_INET6) 250599255Sume tmp.su_sin6.sin6_scope_id = 0; 250699255Sume if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 250756668Sshin hname, sizeof(hname) - 1, NULL, 0, 250856668Sshin NI_NUMERICHOST)) { 2509137983Syar hname[sizeof(hname) - 1] = 0; 251056668Sshin printf(" %s |%d|%s|%d|\r\n", 251156668Sshin ispassive ? "EPSV" : "EPRT", 251299255Sume af, hname, htons(tmp.su_port)); 251356668Sshin } 251456668Sshin } 251556668Sshin } 25161592Srgrimes#undef UC 25171592Srgrimes } else 25181592Srgrimes printf(" No data connection\r\n"); 2519137852Syar reply(211, "End of status."); 25201592Srgrimes} 25211592Srgrimes 25221592Srgrimesvoid 252390148Simpfatalerror(char *s) 25241592Srgrimes{ 25251592Srgrimes 2526137849Syar reply(451, "Error in server: %s", s); 25271592Srgrimes reply(221, "Closing connection due to server error."); 25281592Srgrimes dologout(0); 25291592Srgrimes /* NOTREACHED */ 25301592Srgrimes} 25311592Srgrimes 25321592Srgrimesvoid 25331592Srgrimesreply(int n, const char *fmt, ...) 25341592Srgrimes{ 25351592Srgrimes va_list ap; 253690148Simp 2537129170Stjr (void)printf("%d ", n); 25381592Srgrimes va_start(ap, fmt); 25391592Srgrimes (void)vprintf(fmt, ap); 2540129170Stjr va_end(ap); 25411592Srgrimes (void)printf("\r\n"); 25421592Srgrimes (void)fflush(stdout); 254376096Smarkm if (ftpdebug) { 25441592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 2545129170Stjr va_start(ap, fmt); 25461592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2547129170Stjr va_end(ap); 25481592Srgrimes } 25491592Srgrimes} 25501592Srgrimes 25511592Srgrimesvoid 25521592Srgrimeslreply(int n, const char *fmt, ...) 25531592Srgrimes{ 25541592Srgrimes va_list ap; 255590148Simp 2556129170Stjr (void)printf("%d- ", n); 25571592Srgrimes va_start(ap, fmt); 25581592Srgrimes (void)vprintf(fmt, ap); 2559129170Stjr va_end(ap); 25601592Srgrimes (void)printf("\r\n"); 25611592Srgrimes (void)fflush(stdout); 256276096Smarkm if (ftpdebug) { 25631592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 2564129170Stjr va_start(ap, fmt); 25651592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2566129170Stjr va_end(ap); 25671592Srgrimes } 25681592Srgrimes} 25691592Srgrimes 25701592Srgrimesstatic void 257190148Simpack(char *s) 25721592Srgrimes{ 25731592Srgrimes 25741592Srgrimes reply(250, "%s command successful.", s); 25751592Srgrimes} 25761592Srgrimes 25771592Srgrimesvoid 257890148Simpnack(char *s) 25791592Srgrimes{ 25801592Srgrimes 25811592Srgrimes reply(502, "%s command not implemented.", s); 25821592Srgrimes} 25831592Srgrimes 25841592Srgrimes/* ARGSUSED */ 25851592Srgrimesvoid 258690148Simpyyerror(char *s) 25871592Srgrimes{ 25881592Srgrimes char *cp; 25891592Srgrimes 259017478Smarkm if ((cp = strchr(cbuf,'\n'))) 25911592Srgrimes *cp = '\0'; 2592137852Syar reply(500, "%s: command not understood.", cbuf); 25931592Srgrimes} 25941592Srgrimes 25951592Srgrimesvoid 259690148Simpdelete(char *name) 25971592Srgrimes{ 25981592Srgrimes struct stat st; 25991592Srgrimes 26001592Srgrimes LOGCMD("delete", name); 2601100439Syar if (lstat(name, &st) < 0) { 26021592Srgrimes perror_reply(550, name); 26031592Srgrimes return; 26041592Srgrimes } 2605137847Syar if (S_ISDIR(st.st_mode)) { 26061592Srgrimes if (rmdir(name) < 0) { 26071592Srgrimes perror_reply(550, name); 26081592Srgrimes return; 26091592Srgrimes } 26101592Srgrimes goto done; 26111592Srgrimes } 2612125568Syar if (guest && noguestmod) { 2613137852Syar reply(550, "Operation not permitted."); 2614125568Syar return; 2615125568Syar } 2616125568Syar if (unlink(name) < 0) { 26171592Srgrimes perror_reply(550, name); 26181592Srgrimes return; 26191592Srgrimes } 26201592Srgrimesdone: 26211592Srgrimes ack("DELE"); 26221592Srgrimes} 26231592Srgrimes 26241592Srgrimesvoid 262590148Simpcwd(char *path) 26261592Srgrimes{ 26271592Srgrimes 26281592Srgrimes if (chdir(path) < 0) 26291592Srgrimes perror_reply(550, path); 26301592Srgrimes else 26311592Srgrimes ack("CWD"); 26321592Srgrimes} 26331592Srgrimes 26341592Srgrimesvoid 263590148Simpmakedir(char *name) 26361592Srgrimes{ 2637100878Syar char *s; 26381592Srgrimes 26391592Srgrimes LOGCMD("mkdir", name); 264099195Smdodd if (guest && noguestmkd) 2641137853Syar reply(550, "Operation not permitted."); 264299195Smdodd else if (mkdir(name, 0777) < 0) 26431592Srgrimes perror_reply(550, name); 2644100878Syar else { 2645100878Syar if ((s = doublequote(name)) == NULL) 2646100878Syar fatalerror("Ran out of memory."); 2647100878Syar reply(257, "\"%s\" directory created.", s); 2648100878Syar free(s); 2649100878Syar } 26501592Srgrimes} 26511592Srgrimes 26521592Srgrimesvoid 265390148Simpremovedir(char *name) 26541592Srgrimes{ 26551592Srgrimes 26561592Srgrimes LOGCMD("rmdir", name); 26571592Srgrimes if (rmdir(name) < 0) 26581592Srgrimes perror_reply(550, name); 26591592Srgrimes else 26601592Srgrimes ack("RMD"); 26611592Srgrimes} 26621592Srgrimes 26631592Srgrimesvoid 266490148Simppwd(void) 26651592Srgrimes{ 2666100486Syar char *s, path[MAXPATHLEN + 1]; 26671592Srgrimes 2668137830Syar if (getcwd(path, sizeof(path)) == NULL) 2669137839Syar perror_reply(550, "Get current directory"); 2670100486Syar else { 2671100486Syar if ((s = doublequote(path)) == NULL) 2672100486Syar fatalerror("Ran out of memory."); 2673100486Syar reply(257, "\"%s\" is current directory.", s); 2674100486Syar free(s); 2675100486Syar } 26761592Srgrimes} 26771592Srgrimes 26781592Srgrimeschar * 267990148Simprenamefrom(char *name) 26801592Srgrimes{ 26811592Srgrimes struct stat st; 26821592Srgrimes 2683125569Syar if (guest && noguestmod) { 2684137852Syar reply(550, "Operation not permitted."); 2685125569Syar return (NULL); 2686125569Syar } 2687100439Syar if (lstat(name, &st) < 0) { 26881592Srgrimes perror_reply(550, name); 2689125570Syar return (NULL); 26901592Srgrimes } 2691137852Syar reply(350, "File exists, ready for destination name."); 26921592Srgrimes return (name); 26931592Srgrimes} 26941592Srgrimes 26951592Srgrimesvoid 269690148Simprenamecmd(char *from, char *to) 26971592Srgrimes{ 269817433Spst struct stat st; 26991592Srgrimes 27001592Srgrimes LOGCMD2("rename", from, to); 270117433Spst 270217433Spst if (guest && (stat(to, &st) == 0)) { 2703137852Syar reply(550, "%s: permission denied.", to); 270417433Spst return; 270517433Spst } 270617433Spst 27071592Srgrimes if (rename(from, to) < 0) 27081592Srgrimes perror_reply(550, "rename"); 27091592Srgrimes else 27101592Srgrimes ack("RNTO"); 27111592Srgrimes} 27121592Srgrimes 27131592Srgrimesstatic void 271490148Simpdolog(struct sockaddr *who) 27151592Srgrimes{ 2716137984Syar char who_name[NI_MAXHOST]; 27171592Srgrimes 271856668Sshin realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 2719137983Syar remotehost[sizeof(remotehost) - 1] = 0; 2720137984Syar if (getnameinfo(who, who->sa_len, 2721137984Syar who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) 2722137984Syar *who_name = 0; 2723137984Syar who_name[sizeof(who_name) - 1] = 0; 272456668Sshin 27251592Srgrimes#ifdef SETPROCTITLE 272625283Sdavidn#ifdef VIRTUAL_HOSTING 272725283Sdavidn if (thishost != firsthost) 272825283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 272925283Sdavidn remotehost, hostname); 273025283Sdavidn else 273125283Sdavidn#endif 273225283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected", 273325283Sdavidn remotehost); 273413139Speter setproctitle("%s", proctitle); 27351592Srgrimes#endif /* SETPROCTITLE */ 27361592Srgrimes 273725283Sdavidn if (logging) { 273825283Sdavidn#ifdef VIRTUAL_HOSTING 273925283Sdavidn if (thishost != firsthost) 2740137984Syar syslog(LOG_INFO, "connection from %s (%s) to %s", 2741137984Syar remotehost, who_name, hostname); 274225283Sdavidn else 274325283Sdavidn#endif 2744137984Syar syslog(LOG_INFO, "connection from %s (%s)", 2745137984Syar remotehost, who_name); 274625283Sdavidn } 27471592Srgrimes} 27481592Srgrimes 27491592Srgrimes/* 27501592Srgrimes * Record logout in wtmp file 27511592Srgrimes * and exit with supplied status. 27521592Srgrimes */ 27531592Srgrimesvoid 275490148Simpdologout(int status) 27551592Srgrimes{ 27561592Srgrimes 2757202604Sed if (logged_in && dowtmp) { 2758132893Syar (void) seteuid(0); 2759202604Sed ftpd_logwtmp(wtmpid, NULL, NULL); 27601592Srgrimes } 27611592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 27621592Srgrimes _exit(status); 27631592Srgrimes} 27641592Srgrimes 27651592Srgrimesstatic void 276690148Simpsigurg(int signo) 27671592Srgrimes{ 276889935Syar 276989935Syar recvurg = 1; 277089935Syar} 277189935Syar 277289935Syarstatic void 2773140472Syarmaskurg(int flag) 2774140472Syar{ 2775140472Syar int oerrno; 2776140472Syar sigset_t sset; 2777140472Syar 2778140472Syar if (!transflag) { 2779140472Syar syslog(LOG_ERR, "Internal: maskurg() while no transfer"); 2780140472Syar return; 2781140472Syar } 2782140472Syar oerrno = errno; 2783140472Syar sigemptyset(&sset); 2784140472Syar sigaddset(&sset, SIGURG); 2785140472Syar sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); 2786140472Syar errno = oerrno; 2787140472Syar} 2788140472Syar 2789140472Syarstatic void 2790140472Syarflagxfer(int flag) 2791140472Syar{ 2792140472Syar 2793140472Syar if (flag) { 2794141967Syar if (transflag) 2795141967Syar syslog(LOG_ERR, "Internal: flagxfer(1): " 2796141967Syar "transfer already under way"); 2797141966Syar transflag = 1; 2798141966Syar maskurg(0); 2799140472Syar recvurg = 0; 2800141966Syar } else { 2801141967Syar if (!transflag) 2802141967Syar syslog(LOG_ERR, "Internal: flagxfer(0): " 2803141967Syar "no active transfer"); 2804141966Syar maskurg(1); 2805140472Syar transflag = 0; 2806141966Syar } 2807140472Syar} 2808140472Syar 2809140472Syar/* 2810140472Syar * Returns 0 if OK to resume or -1 if abort requested. 2811140472Syar */ 2812140472Syarstatic int 281390148Simpmyoob(void) 281489935Syar{ 28151592Srgrimes char *cp; 2816186405Scperciva int ret; 28171592Srgrimes 2818140472Syar if (!transflag) { 2819140472Syar syslog(LOG_ERR, "Internal: myoob() while no transfer"); 2820140472Syar return (0); 2821140472Syar } 28221592Srgrimes cp = tmpline; 2823186405Scperciva ret = getline(cp, 7, stdin); 2824186405Scperciva if (ret == -1) { 28251592Srgrimes reply(221, "You could at least say goodbye."); 28261592Srgrimes dologout(0); 2827186405Scperciva } else if (ret == -2) { 2828186405Scperciva /* Ignore truncated command. */ 2829186405Scperciva return (0); 28301592Srgrimes } 28311592Srgrimes upper(cp); 28321592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 28331592Srgrimes tmpline[0] = '\0'; 28341592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 2835137852Syar reply(226, "Abort successful."); 2836140472Syar return (-1); 28371592Srgrimes } 28381592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 283951192Smharo tmpline[0] = '\0'; 2840132930Syar if (file_size != -1) 2841137852Syar reply(213, "Status: %jd of %jd bytes transferred.", 2842132929Syar (intmax_t)byte_count, (intmax_t)file_size); 28431592Srgrimes else 2844137852Syar reply(213, "Status: %jd bytes transferred.", 2845132929Syar (intmax_t)byte_count); 28461592Srgrimes } 2847140472Syar return (0); 28481592Srgrimes} 28491592Srgrimes 28501592Srgrimes/* 28511592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 28521592Srgrimes * the PASV command in RFC959. However, it has been blessed as 28531592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 28541592Srgrimes * with Rick Adams on 25 Jan 89. 28551592Srgrimes */ 28561592Srgrimesvoid 285790148Simppassive(void) 28581592Srgrimes{ 2859141918Sstefanf socklen_t len; 2860141918Sstefanf int on; 28611592Srgrimes char *p, *a; 28621592Srgrimes 286317433Spst if (pdata >= 0) /* close old port if one set */ 286417433Spst close(pdata); 286517433Spst 286656668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 28671592Srgrimes if (pdata < 0) { 28681592Srgrimes perror_reply(425, "Can't open passive connection"); 28691592Srgrimes return; 28701592Srgrimes } 2871100615Syar on = 1; 2872100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2873100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 28749933Spst 2875132893Syar (void) seteuid(0); 287617433Spst 287719903Spst#ifdef IP_PORTRANGE 287856668Sshin if (ctrl_addr.su_family == AF_INET) { 2879100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2880100615Syar : IP_PORTRANGE_DEFAULT; 288119903Spst 288219903Spst if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2883100612Syar &on, sizeof(on)) < 0) 288419903Spst goto pasv_error; 28851592Srgrimes } 288619903Spst#endif 288760929Snsayer#ifdef IPV6_PORTRANGE 288860929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2889100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2890100615Syar : IPV6_PORTRANGE_DEFAULT; 28919933Spst 289260929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2893100612Syar &on, sizeof(on)) < 0) 289460929Snsayer goto pasv_error; 289560929Snsayer } 289660929Snsayer#endif 289760929Snsayer 289816033Speter pasv_addr = ctrl_addr; 289956668Sshin pasv_addr.su_port = 0; 290056668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 290116033Speter goto pasv_error; 290217433Spst 2903132893Syar (void) seteuid(pw->pw_uid); 290416033Speter 29051592Srgrimes len = sizeof(pasv_addr); 29061592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 29071592Srgrimes goto pasv_error; 29081592Srgrimes if (listen(pdata, 1) < 0) 29091592Srgrimes goto pasv_error; 291056668Sshin if (pasv_addr.su_family == AF_INET) 291156668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 291256668Sshin else if (pasv_addr.su_family == AF_INET6 && 291356668Sshin IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 291456668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 291556668Sshin else 291656668Sshin goto pasv_error; 291756668Sshin 291856668Sshin p = (char *) &pasv_addr.su_port; 29191592Srgrimes 29201592Srgrimes#define UC(b) (((int) b) & 0xff) 29211592Srgrimes 29221592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 29231592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 29241592Srgrimes return; 29251592Srgrimes 29261592Srgrimespasv_error: 2927132893Syar (void) seteuid(pw->pw_uid); 29281592Srgrimes (void) close(pdata); 29291592Srgrimes pdata = -1; 29301592Srgrimes perror_reply(425, "Can't open passive connection"); 29311592Srgrimes return; 29321592Srgrimes} 29331592Srgrimes 29341592Srgrimes/* 293556668Sshin * Long Passive defined in RFC 1639. 293656668Sshin * 228 Entering Long Passive Mode 293756668Sshin * (af, hal, h1, h2, h3,..., pal, p1, p2...) 293856668Sshin */ 293956668Sshin 294056668Sshinvoid 294190148Simplong_passive(char *cmd, int pf) 294256668Sshin{ 2943141918Sstefanf socklen_t len; 2944141918Sstefanf int on; 294556668Sshin char *p, *a; 294656668Sshin 294756668Sshin if (pdata >= 0) /* close old port if one set */ 294856668Sshin close(pdata); 294956668Sshin 295056668Sshin if (pf != PF_UNSPEC) { 295156668Sshin if (ctrl_addr.su_family != pf) { 295256668Sshin switch (ctrl_addr.su_family) { 295356668Sshin case AF_INET: 295456668Sshin pf = 1; 295556668Sshin break; 295656668Sshin case AF_INET6: 295756668Sshin pf = 2; 295856668Sshin break; 295956668Sshin default: 296056668Sshin pf = 0; 296156668Sshin break; 296256668Sshin } 296356668Sshin /* 296456668Sshin * XXX 296556668Sshin * only EPRT/EPSV ready clients will understand this 296656668Sshin */ 296756668Sshin if (strcmp(cmd, "EPSV") == 0 && pf) { 296856668Sshin reply(522, "Network protocol mismatch, " 296956668Sshin "use (%d)", pf); 297056668Sshin } else 2971137852Syar reply(501, "Network protocol mismatch."); /*XXX*/ 297256668Sshin 297356668Sshin return; 297456668Sshin } 297556668Sshin } 297656668Sshin 297756668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 297856668Sshin if (pdata < 0) { 297956668Sshin perror_reply(425, "Can't open passive connection"); 298056668Sshin return; 298156668Sshin } 2982100615Syar on = 1; 2983100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2984100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 298556668Sshin 2986132893Syar (void) seteuid(0); 298756668Sshin 298856668Sshin pasv_addr = ctrl_addr; 298956668Sshin pasv_addr.su_port = 0; 299056668Sshin len = pasv_addr.su_len; 299156668Sshin 299260929Snsayer#ifdef IP_PORTRANGE 299360929Snsayer if (ctrl_addr.su_family == AF_INET) { 2994100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2995100615Syar : IP_PORTRANGE_DEFAULT; 299660929Snsayer 299760929Snsayer if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2998100612Syar &on, sizeof(on)) < 0) 299960929Snsayer goto pasv_error; 300060929Snsayer } 300160929Snsayer#endif 300260929Snsayer#ifdef IPV6_PORTRANGE 300360929Snsayer if (ctrl_addr.su_family == AF_INET6) { 3004100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 3005100615Syar : IPV6_PORTRANGE_DEFAULT; 300660929Snsayer 300760929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 3008100612Syar &on, sizeof(on)) < 0) 300960929Snsayer goto pasv_error; 301060929Snsayer } 301160929Snsayer#endif 301260929Snsayer 301356668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 301456668Sshin goto pasv_error; 301556668Sshin 3016132893Syar (void) seteuid(pw->pw_uid); 301756668Sshin 301856668Sshin if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 301956668Sshin goto pasv_error; 302056668Sshin if (listen(pdata, 1) < 0) 302156668Sshin goto pasv_error; 302256668Sshin 302356668Sshin#define UC(b) (((int) b) & 0xff) 302456668Sshin 302556668Sshin if (strcmp(cmd, "LPSV") == 0) { 302656668Sshin p = (char *)&pasv_addr.su_port; 302756668Sshin switch (pasv_addr.su_family) { 302856668Sshin case AF_INET: 302956668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 303056668Sshin v4_reply: 303156668Sshin reply(228, 303256668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 303356668Sshin 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 303456668Sshin 2, UC(p[0]), UC(p[1])); 303556668Sshin return; 303656668Sshin case AF_INET6: 303756668Sshin if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 303856668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 303956668Sshin goto v4_reply; 304056668Sshin } 304156668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr; 304256668Sshin reply(228, 304356668Sshin"Entering Long Passive Mode " 304456668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 304556668Sshin 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 304656668Sshin UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 304756668Sshin UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 304856668Sshin UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 304956668Sshin 2, UC(p[0]), UC(p[1])); 305056668Sshin return; 305156668Sshin } 305256668Sshin } else if (strcmp(cmd, "EPSV") == 0) { 305356668Sshin switch (pasv_addr.su_family) { 305456668Sshin case AF_INET: 305556668Sshin case AF_INET6: 305656668Sshin reply(229, "Entering Extended Passive Mode (|||%d|)", 305756668Sshin ntohs(pasv_addr.su_port)); 305856668Sshin return; 305956668Sshin } 306056668Sshin } else { 306156668Sshin /* more proper error code? */ 306256668Sshin } 306356668Sshin 306456668Sshinpasv_error: 3065132893Syar (void) seteuid(pw->pw_uid); 306656668Sshin (void) close(pdata); 306756668Sshin pdata = -1; 306856668Sshin perror_reply(425, "Can't open passive connection"); 306956668Sshin return; 307056668Sshin} 307156668Sshin 307256668Sshin/* 3073101537Syar * Generate unique name for file with basename "local" 3074101537Syar * and open the file in order to avoid possible races. 3075101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 3076101537Syar * Return descriptor to the file, set "name" to its name. 3077101537Syar * 30781592Srgrimes * Generates failure reply on error. 30791592Srgrimes */ 3080101537Syarstatic int 3081101537Syarguniquefd(char *local, char **name) 30821592Srgrimes{ 30831592Srgrimes static char new[MAXPATHLEN]; 30841592Srgrimes struct stat st; 3085101537Syar char *cp; 30861592Srgrimes int count; 3087101537Syar int fd; 30881592Srgrimes 30891592Srgrimes cp = strrchr(local, '/'); 30901592Srgrimes if (cp) 30911592Srgrimes *cp = '\0'; 30921592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 30931592Srgrimes perror_reply(553, cp ? local : "."); 3094101537Syar return (-1); 30951592Srgrimes } 3096101537Syar if (cp) { 3097101537Syar /* 3098101537Syar * Let not overwrite dirname with counter suffix. 3099101537Syar * -4 is for /nn\0 3100101537Syar * In this extreme case dot won't be put in front of suffix. 3101101537Syar */ 3102101537Syar if (strlen(local) > sizeof(new) - 4) { 3103137852Syar reply(553, "Pathname too long."); 3104101537Syar return (-1); 3105101537Syar } 31061592Srgrimes *cp = '/'; 3107101537Syar } 310831973Simp /* -4 is for the .nn<null> we put on the end below */ 310931973Simp (void) snprintf(new, sizeof(new) - 4, "%s", local); 31101592Srgrimes cp = new + strlen(new); 3111101537Syar /* 3112101537Syar * Don't generate dotfile unless requested explicitly. 3113101537Syar * This covers the case when basename gets truncated off 3114101537Syar * by buffer size. 3115101537Syar */ 3116101537Syar if (cp > new && cp[-1] != '/') 3117101537Syar *cp++ = '.'; 3118101537Syar for (count = 0; count < 100; count++) { 3119101537Syar /* At count 0 try unmodified name */ 3120101537Syar if (count) 3121101537Syar (void)sprintf(cp, "%d", count); 3122101537Syar if ((fd = open(count ? new : local, 3123101537Syar O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 3124101537Syar *name = count ? new : local; 3125101537Syar return (fd); 3126101537Syar } 3127110046Syar if (errno != EEXIST) { 3128110307Syar perror_reply(553, count ? new : local); 3129110046Syar return (-1); 3130110046Syar } 31311592Srgrimes } 31321592Srgrimes reply(452, "Unique file name cannot be created."); 3133101537Syar return (-1); 31341592Srgrimes} 31351592Srgrimes 31361592Srgrimes/* 31371592Srgrimes * Format and send reply containing system error number. 31381592Srgrimes */ 31391592Srgrimesvoid 314090148Simpperror_reply(int code, char *string) 31411592Srgrimes{ 31421592Srgrimes 31431592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 31441592Srgrimes} 31451592Srgrimes 31461592Srgrimesstatic char *onefile[] = { 31471592Srgrimes "", 31481592Srgrimes 0 31491592Srgrimes}; 31501592Srgrimes 31511592Srgrimesvoid 315290148Simpsend_file_list(char *whichf) 31531592Srgrimes{ 31541592Srgrimes struct stat st; 31551592Srgrimes DIR *dirp = NULL; 31561592Srgrimes struct dirent *dir; 31571592Srgrimes FILE *dout = NULL; 31581592Srgrimes char **dirlist, *dirname; 31591592Srgrimes int simple = 0; 31601592Srgrimes int freeglob = 0; 31611592Srgrimes glob_t gl; 31621592Srgrimes 31631592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 3164100222Smikeh int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 31651592Srgrimes 31661592Srgrimes memset(&gl, 0, sizeof(gl)); 316774470Sjlemon gl.gl_matchc = MAXGLOBARGS; 316880525Smikeh flags |= GLOB_LIMIT; 31691592Srgrimes freeglob = 1; 31701592Srgrimes if (glob(whichf, flags, 0, &gl)) { 3171137852Syar reply(550, "No matching files found."); 31721592Srgrimes goto out; 31731592Srgrimes } else if (gl.gl_pathc == 0) { 31741592Srgrimes errno = ENOENT; 31751592Srgrimes perror_reply(550, whichf); 31761592Srgrimes goto out; 31771592Srgrimes } 31781592Srgrimes dirlist = gl.gl_pathv; 31791592Srgrimes } else { 31801592Srgrimes onefile[0] = whichf; 31811592Srgrimes dirlist = onefile; 31821592Srgrimes simple = 1; 31831592Srgrimes } 31841592Srgrimes 318517478Smarkm while ((dirname = *dirlist++)) { 31861592Srgrimes if (stat(dirname, &st) < 0) { 31871592Srgrimes /* 31881592Srgrimes * If user typed "ls -l", etc, and the client 31891592Srgrimes * used NLST, do what the user meant. 31901592Srgrimes */ 31911592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 3192140472Syar dout == NULL) 319325165Sdavidn retrieve(_PATH_LS " %s", dirname); 3194140472Syar else 3195140472Syar perror_reply(550, whichf); 31961592Srgrimes goto out; 31971592Srgrimes } 31981592Srgrimes 31991592Srgrimes if (S_ISREG(st.st_mode)) { 32001592Srgrimes if (dout == NULL) { 3201132930Syar dout = dataconn("file list", -1, "w"); 32021592Srgrimes if (dout == NULL) 32031592Srgrimes goto out; 3204140472Syar STARTXFER; 32051592Srgrimes } 3206140472Syar START_UNSAFE; 32071592Srgrimes fprintf(dout, "%s%s\n", dirname, 32081592Srgrimes type == TYPE_A ? "\r" : ""); 3209140472Syar END_UNSAFE; 3210140472Syar if (ferror(dout)) 3211140472Syar goto data_err; 3212140472Syar byte_count += strlen(dirname) + 3213140472Syar (type == TYPE_A ? 2 : 1); 3214140472Syar CHECKOOB(goto abrt); 32151592Srgrimes continue; 32161592Srgrimes } else if (!S_ISDIR(st.st_mode)) 32171592Srgrimes continue; 32181592Srgrimes 32191592Srgrimes if ((dirp = opendir(dirname)) == NULL) 32201592Srgrimes continue; 32211592Srgrimes 32221592Srgrimes while ((dir = readdir(dirp)) != NULL) { 32231592Srgrimes char nbuf[MAXPATHLEN]; 32241592Srgrimes 3225140472Syar CHECKOOB(goto abrt); 322689935Syar 32271592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 32281592Srgrimes continue; 32291592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 32301592Srgrimes dir->d_namlen == 2) 32311592Srgrimes continue; 32321592Srgrimes 323399213Smaxim snprintf(nbuf, sizeof(nbuf), 323431973Simp "%s/%s", dirname, dir->d_name); 32351592Srgrimes 32361592Srgrimes /* 32371592Srgrimes * We have to do a stat to insure it's 32381592Srgrimes * not a directory or special file. 32391592Srgrimes */ 32401592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 32411592Srgrimes S_ISREG(st.st_mode))) { 32421592Srgrimes if (dout == NULL) { 3243132930Syar dout = dataconn("file list", -1, "w"); 32441592Srgrimes if (dout == NULL) 32451592Srgrimes goto out; 3246140472Syar STARTXFER; 32471592Srgrimes } 3248140472Syar START_UNSAFE; 32491592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 32501592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 32511592Srgrimes type == TYPE_A ? "\r" : ""); 32521592Srgrimes else 32531592Srgrimes fprintf(dout, "%s%s\n", nbuf, 32541592Srgrimes type == TYPE_A ? "\r" : ""); 3255140472Syar END_UNSAFE; 3256140472Syar if (ferror(dout)) 3257140472Syar goto data_err; 3258140472Syar byte_count += strlen(nbuf) + 3259140472Syar (type == TYPE_A ? 2 : 1); 3260140472Syar CHECKOOB(goto abrt); 32611592Srgrimes } 32621592Srgrimes } 32631592Srgrimes (void) closedir(dirp); 3264140472Syar dirp = NULL; 32651592Srgrimes } 32661592Srgrimes 32671592Srgrimes if (dout == NULL) 32681592Srgrimes reply(550, "No files found."); 3269140472Syar else if (ferror(dout)) 3270140472Syardata_err: perror_reply(550, "Data connection"); 32711592Srgrimes else 32721592Srgrimes reply(226, "Transfer complete."); 3273140472Syarout: 3274140472Syar if (dout) { 3275140472Syar ENDXFER; 3276140472Syarabrt: 32771592Srgrimes (void) fclose(dout); 3278140472Syar data = -1; 3279140472Syar pdata = -1; 3280140472Syar } 3281140472Syar if (dirp) 3282140472Syar (void) closedir(dirp); 32831592Srgrimes if (freeglob) { 32841592Srgrimes freeglob = 0; 32851592Srgrimes globfree(&gl); 32861592Srgrimes } 32871592Srgrimes} 32881592Srgrimes 328915196Sdgvoid 329090148Simpreapchild(int signo) 329115196Sdg{ 3292137830Syar while (waitpid(-1, NULL, WNOHANG) > 0); 329315196Sdg} 329415196Sdg 329513139Speter#ifdef OLD_SETPROCTITLE 32961592Srgrimes/* 32971592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 32981592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 32991592Srgrimes * have much of an environment or arglist to overwrite. 33001592Srgrimes */ 33011592Srgrimesvoid 33021592Srgrimessetproctitle(const char *fmt, ...) 33031592Srgrimes{ 33041592Srgrimes int i; 33051592Srgrimes va_list ap; 33061592Srgrimes char *p, *bp, ch; 33071592Srgrimes char buf[LINE_MAX]; 33081592Srgrimes 33091592Srgrimes va_start(ap, fmt); 33101592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 33111592Srgrimes 33121592Srgrimes /* make ps print our process name */ 33131592Srgrimes p = Argv[0]; 33141592Srgrimes *p++ = '-'; 33151592Srgrimes 33161592Srgrimes i = strlen(buf); 33171592Srgrimes if (i > LastArgv - p - 2) { 33181592Srgrimes i = LastArgv - p - 2; 33191592Srgrimes buf[i] = '\0'; 33201592Srgrimes } 33211592Srgrimes bp = buf; 33221592Srgrimes while (ch = *bp++) 33231592Srgrimes if (ch != '\n' && ch != '\r') 33241592Srgrimes *p++ = ch; 33251592Srgrimes while (p < LastArgv) 33261592Srgrimes *p++ = ' '; 33271592Srgrimes} 332813139Speter#endif /* OLD_SETPROCTITLE */ 33296740Sguido 333017433Spststatic void 3331137848Syarappendf(char **strp, char *fmt, ...) 3332137848Syar{ 3333137848Syar va_list ap; 3334137848Syar char *ostr, *p; 3335137848Syar 3336137848Syar va_start(ap, fmt); 3337137848Syar vasprintf(&p, fmt, ap); 3338137848Syar va_end(ap); 3339137848Syar if (p == NULL) 3340137848Syar fatalerror("Ran out of memory."); 3341137848Syar if (*strp == NULL) 3342137848Syar *strp = p; 3343137848Syar else { 3344137848Syar ostr = *strp; 3345137848Syar asprintf(strp, "%s%s", ostr, p); 3346137848Syar if (*strp == NULL) 3347137848Syar fatalerror("Ran out of memory."); 3348137848Syar free(ostr); 3349137848Syar } 3350137848Syar} 3351137848Syar 3352137848Syarstatic void 3353137848Syarlogcmd(char *cmd, char *file1, char *file2, off_t cnt) 3354137848Syar{ 3355137848Syar char *msg = NULL; 3356137848Syar char wd[MAXPATHLEN + 1]; 3357137848Syar 3358137848Syar if (logging <= 1) 3359137848Syar return; 3360137848Syar 3361137985Syar if (getcwd(wd, sizeof(wd) - 1) == NULL) 3362137985Syar strcpy(wd, strerror(errno)); 3363137985Syar 3364137848Syar appendf(&msg, "%s", cmd); 3365137848Syar if (file1) 3366137848Syar appendf(&msg, " %s", file1); 3367137848Syar if (file2) 3368137848Syar appendf(&msg, " %s", file2); 3369137848Syar if (cnt >= 0) 3370137848Syar appendf(&msg, " = %jd bytes", (intmax_t)cnt); 3371137985Syar appendf(&msg, " (wd: %s", wd); 3372137862Syar if (guest || dochroot) 3373137985Syar appendf(&msg, "; chrooted"); 3374137985Syar appendf(&msg, ")"); 3375137848Syar syslog(LOG_INFO, "%s", msg); 3376137848Syar free(msg); 3377137848Syar} 3378137848Syar 3379137848Syarstatic void 338090148Simplogxfer(char *name, off_t size, time_t start) 33816740Sguido{ 3382137145Syar char buf[MAXPATHLEN + 1024]; 33836740Sguido char path[MAXPATHLEN + 1]; 338436612Sjb time_t now; 33856740Sguido 3386137145Syar if (statfd >= 0) { 33876740Sguido time(&now); 3388137145Syar if (realpath(name, path) == NULL) { 3389137145Syar syslog(LOG_NOTICE, "realpath failed on %s: %m", path); 3390137145Syar return; 3391137145Syar } 3392137145Syar snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", 33936740Sguido ctime(&now)+4, ident, remotehost, 3394137145Syar path, (intmax_t)size, 339582792Sache (long)(now - start + (now == start))); 33966740Sguido write(statfd, buf, strlen(buf)); 33976740Sguido } 33986740Sguido} 3399100486Syar 3400100486Syarstatic char * 3401100486Syardoublequote(char *s) 3402100486Syar{ 3403100486Syar int n; 3404100486Syar char *p, *s2; 3405100486Syar 3406100486Syar for (p = s, n = 0; *p; p++) 3407100486Syar if (*p == '"') 3408100486Syar n++; 3409100486Syar 3410100486Syar if ((s2 = malloc(p - s + n + 1)) == NULL) 3411100486Syar return (NULL); 3412100486Syar 3413100486Syar for (p = s2; *s; s++, p++) { 3414100486Syar if ((*p = *s) == '"') 3415100486Syar *(++p) = '"'; 3416100486Syar } 3417100486Syar *p = '\0'; 3418100486Syar 3419100486Syar return (s2); 3420100486Syar} 3421120059Sume 3422120059Sume/* setup server socket for specified address family */ 3423120059Sume/* if af is PF_UNSPEC more than one socket may be returned */ 3424120059Sume/* the returned list is dynamically allocated, so caller needs to free it */ 3425120059Sumestatic int * 3426120059Sumesocksetup(int af, char *bindname, const char *bindport) 3427120059Sume{ 3428120059Sume struct addrinfo hints, *res, *r; 3429120059Sume int error, maxs, *s, *socks; 3430120059Sume const int on = 1; 3431120059Sume 3432120059Sume memset(&hints, 0, sizeof(hints)); 3433120059Sume hints.ai_flags = AI_PASSIVE; 3434120059Sume hints.ai_family = af; 3435120059Sume hints.ai_socktype = SOCK_STREAM; 3436120059Sume error = getaddrinfo(bindname, bindport, &hints, &res); 3437120059Sume if (error) { 3438120059Sume syslog(LOG_ERR, "%s", gai_strerror(error)); 3439120059Sume if (error == EAI_SYSTEM) 3440120059Sume syslog(LOG_ERR, "%s", strerror(errno)); 3441120059Sume return NULL; 3442120059Sume } 3443120059Sume 3444120059Sume /* Count max number of sockets we may open */ 3445120059Sume for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 3446120059Sume ; 3447120059Sume socks = malloc((maxs + 1) * sizeof(int)); 3448120059Sume if (!socks) { 3449120059Sume freeaddrinfo(res); 3450120059Sume syslog(LOG_ERR, "couldn't allocate memory for sockets"); 3451120059Sume return NULL; 3452120059Sume } 3453120059Sume 3454120059Sume *socks = 0; /* num of sockets counter at start of array */ 3455120059Sume s = socks + 1; 3456120059Sume for (r = res; r; r = r->ai_next) { 3457120059Sume *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 3458120059Sume if (*s < 0) { 3459120059Sume syslog(LOG_DEBUG, "control socket: %m"); 3460120059Sume continue; 3461120059Sume } 3462120059Sume if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 3463120059Sume &on, sizeof(on)) < 0) 3464120059Sume syslog(LOG_WARNING, 3465120059Sume "control setsockopt (SO_REUSEADDR): %m"); 3466120059Sume if (r->ai_family == AF_INET6) { 3467120059Sume if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 3468120059Sume &on, sizeof(on)) < 0) 3469120059Sume syslog(LOG_WARNING, 3470120059Sume "control setsockopt (IPV6_V6ONLY): %m"); 3471120059Sume } 3472120059Sume if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 3473120059Sume syslog(LOG_DEBUG, "control bind: %m"); 3474120059Sume close(*s); 3475120059Sume continue; 3476120059Sume } 3477120059Sume (*socks)++; 3478120059Sume s++; 3479120059Sume } 3480120059Sume 3481120059Sume if (res) 3482120059Sume freeaddrinfo(res); 3483120059Sume 3484120059Sume if (*socks == 0) { 3485120059Sume syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); 3486120059Sume free(socks); 3487120059Sume return NULL; 3488120059Sume } 3489120059Sume return(socks); 3490120059Sume} 3491