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: stable/10/libexec/ftpd/ftpd.c 327019 2017-12-20 07:18:07Z delphij $"); 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 423325472Seugen /* handle filesize limit gracefully */ 424325472Seugen sa.sa_handler = SIG_IGN; 425325472Seugen (void)sigaction(SIGXFSZ, &sa, NULL); 426325472Seugen 42715196Sdg if (daemon_mode) { 428120059Sume int *ctl_sock, fd, maxfd = -1, nfds, i; 429120059Sume fd_set defreadfds, readfds; 430120059Sume pid_t pid; 431154630Syar struct pidfh *pfh; 43215196Sdg 433154630Syar if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) { 434154630Syar if (errno == EEXIST) { 435154630Syar syslog(LOG_ERR, "%s already running, pid %d", 436154630Syar getprogname(), (int)pid); 437154630Syar exit(1); 438154630Syar } 439154630Syar syslog(LOG_WARNING, "pidfile_open: %m"); 440154630Syar } 441154630Syar 44215196Sdg /* 44315196Sdg * Detach from parent. 44415196Sdg */ 44515196Sdg if (daemon(1, 1) < 0) { 44615196Sdg syslog(LOG_ERR, "failed to become a daemon"); 44715196Sdg exit(1); 44815196Sdg } 449154630Syar 450154630Syar if (pfh != NULL && pidfile_write(pfh) == -1) 451154630Syar syslog(LOG_WARNING, "pidfile_write: %m"); 452154630Syar 45389935Syar sa.sa_handler = reapchild; 45489935Syar (void)sigaction(SIGCHLD, &sa, NULL); 45556668Sshin 456156156Sume#ifdef VIRTUAL_HOSTING 457156156Sume inithosts(family); 458156156Sume#endif 459156156Sume 46015196Sdg /* 46115196Sdg * Open a socket, bind it to the FTP port, and start 46215196Sdg * listening. 46315196Sdg */ 464120059Sume ctl_sock = socksetup(family, bindname, bindport); 465120059Sume if (ctl_sock == NULL) 46615196Sdg exit(1); 467120059Sume 468120059Sume FD_ZERO(&defreadfds); 469120059Sume for (i = 1; i <= *ctl_sock; i++) { 470120059Sume FD_SET(ctl_sock[i], &defreadfds); 471120059Sume if (listen(ctl_sock[i], 32) < 0) { 472120059Sume syslog(LOG_ERR, "control listen: %m"); 473120059Sume exit(1); 474120059Sume } 475120059Sume if (maxfd < ctl_sock[i]) 476120059Sume maxfd = ctl_sock[i]; 47715196Sdg } 478120059Sume 47915196Sdg /* 48015196Sdg * Loop forever accepting connection requests and forking off 48115196Sdg * children to handle them. 48215196Sdg */ 48315196Sdg while (1) { 484120059Sume FD_COPY(&defreadfds, &readfds); 485120059Sume nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); 486120059Sume if (nfds <= 0) { 487120059Sume if (nfds < 0 && errno != EINTR) 488120059Sume syslog(LOG_WARNING, "select: %m"); 489120059Sume continue; 490120059Sume } 491120059Sume 492120059Sume pid = -1; 493120059Sume for (i = 1; i <= *ctl_sock; i++) 494120059Sume if (FD_ISSET(ctl_sock[i], &readfds)) { 495120059Sume addrlen = sizeof(his_addr); 496120059Sume fd = accept(ctl_sock[i], 497120059Sume (struct sockaddr *)&his_addr, 498120059Sume &addrlen); 499154634Syar if (fd == -1) { 500154634Syar syslog(LOG_WARNING, 501154634Syar "accept: %m"); 502154634Syar continue; 503135737Smaxim } 504154634Syar switch (pid = fork()) { 505154634Syar case 0: 506154634Syar /* child */ 507154634Syar (void) dup2(fd, 0); 508154634Syar (void) dup2(fd, 1); 509154634Syar (void) close(fd); 510154634Syar for (i = 1; i <= *ctl_sock; i++) 511154634Syar close(ctl_sock[i]); 512154634Syar if (pfh != NULL) 513154634Syar pidfile_close(pfh); 514154634Syar goto gotchild; 515154634Syar case -1: 516154634Syar syslog(LOG_WARNING, "fork: %m"); 517154634Syar /* FALLTHROUGH */ 518154634Syar default: 519154634Syar close(fd); 520154634Syar } 521120059Sume } 52215196Sdg } 52315196Sdg } else { 52415196Sdg addrlen = sizeof(his_addr); 52515196Sdg if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 52615196Sdg syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 52715196Sdg exit(1); 52815196Sdg } 529156156Sume 530156156Sume#ifdef VIRTUAL_HOSTING 531156156Sume if (his_addr.su_family == AF_INET6 && 532156156Sume IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 533156156Sume family = AF_INET; 534156156Sume else 535156156Sume family = his_addr.su_family; 536156156Sume inithosts(family); 537156156Sume#endif 53815196Sdg } 53915196Sdg 540154634Syargotchild: 54189935Syar sa.sa_handler = SIG_DFL; 54289935Syar (void)sigaction(SIGCHLD, &sa, NULL); 5431592Srgrimes 54489935Syar sa.sa_handler = sigurg; 54589935Syar sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ 54689935Syar (void)sigaction(SIGURG, &sa, NULL); 54789935Syar 54889935Syar sigfillset(&sa.sa_mask); /* block all signals in handler */ 54989935Syar sa.sa_flags = SA_RESTART; 55089935Syar sa.sa_handler = sigquit; 55189935Syar (void)sigaction(SIGHUP, &sa, NULL); 55289935Syar (void)sigaction(SIGINT, &sa, NULL); 55389935Syar (void)sigaction(SIGQUIT, &sa, NULL); 55489935Syar (void)sigaction(SIGTERM, &sa, NULL); 55589935Syar 55689935Syar sa.sa_handler = lostconn; 55789935Syar (void)sigaction(SIGPIPE, &sa, NULL); 55889935Syar 55915196Sdg addrlen = sizeof(ctrl_addr); 56015196Sdg if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 56115196Sdg syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 56215196Sdg exit(1); 56315196Sdg } 564109742Syar dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ 56525283Sdavidn#ifdef VIRTUAL_HOSTING 56625283Sdavidn /* select our identity from virtual host table */ 56756668Sshin selecthost(&ctrl_addr); 56825283Sdavidn#endif 56915196Sdg#ifdef IP_TOS 57056668Sshin if (ctrl_addr.su_family == AF_INET) 57156668Sshin { 57215196Sdg tos = IPTOS_LOWDELAY; 573100612Syar if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 574100609Syar syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); 57556668Sshin } 57615196Sdg#endif 57735482Sdg /* 57835482Sdg * Disable Nagle on the control channel so that we don't have to wait 57935482Sdg * for peer's ACK before issuing our next reply. 58035482Sdg */ 58135482Sdg if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 582100609Syar syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); 58335482Sdg 58456668Sshin data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); 58515196Sdg 586202209Sed (void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid()); 58717435Spst 5881592Srgrimes /* Try to handle urgent data inline */ 5891592Srgrimes#ifdef SO_OOBINLINE 590100612Syar if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) 591100609Syar syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); 5921592Srgrimes#endif 5931592Srgrimes 5941592Srgrimes#ifdef F_SETOWN 5951592Srgrimes if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 5961592Srgrimes syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 5971592Srgrimes#endif 59856668Sshin dolog((struct sockaddr *)&his_addr); 5991592Srgrimes /* 6001592Srgrimes * Set up default state 6011592Srgrimes */ 6021592Srgrimes data = -1; 6031592Srgrimes type = TYPE_A; 6041592Srgrimes form = FORM_N; 6051592Srgrimes stru = STRU_F; 6061592Srgrimes mode = MODE_S; 6071592Srgrimes tmpline[0] = '\0'; 6081592Srgrimes 6091592Srgrimes /* If logins are disabled, print out the message. */ 6101592Srgrimes if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 6111592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6121592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6131592Srgrimes *cp = '\0'; 6141592Srgrimes lreply(530, "%s", line); 6151592Srgrimes } 6161592Srgrimes (void) fflush(stdout); 6171592Srgrimes (void) fclose(fd); 6181592Srgrimes reply(530, "System not available."); 6191592Srgrimes exit(0); 6201592Srgrimes } 62125283Sdavidn#ifdef VIRTUAL_HOSTING 622130428Sobrien fd = fopen(thishost->welcome, "r"); 62325283Sdavidn#else 624130428Sobrien fd = fopen(_PATH_FTPWELCOME, "r"); 62525283Sdavidn#endif 626130428Sobrien if (fd != NULL) { 6271592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6281592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6291592Srgrimes *cp = '\0'; 6301592Srgrimes lreply(220, "%s", line); 6311592Srgrimes } 6321592Srgrimes (void) fflush(stdout); 6331592Srgrimes (void) fclose(fd); 6341592Srgrimes /* reply(220,) must follow */ 6351592Srgrimes } 63625283Sdavidn#ifndef VIRTUAL_HOSTING 63727650Sdavidn if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 63876096Smarkm fatalerror("Ran out of memory."); 639137983Syar if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 640137983Syar hostname[0] = '\0'; 64145422Sbrian hostname[MAXHOSTNAMELEN - 1] = '\0'; 64225283Sdavidn#endif 643110037Syar if (hostinfo) 644110037Syar reply(220, "%s FTP server (%s) ready.", hostname, version); 645110037Syar else 646110037Syar reply(220, "FTP server ready."); 6471592Srgrimes for (;;) 6481592Srgrimes (void) yyparse(); 6491592Srgrimes /* NOTREACHED */ 6501592Srgrimes} 6511592Srgrimes 6521592Srgrimesstatic void 65390148Simplostconn(int signo) 6541592Srgrimes{ 6551592Srgrimes 65676096Smarkm if (ftpdebug) 6571592Srgrimes syslog(LOG_DEBUG, "lost connection"); 65831329Scharnier dologout(1); 6591592Srgrimes} 6601592Srgrimes 66189935Syarstatic void 66290148Simpsigquit(int signo) 66389935Syar{ 66489935Syar 66589935Syar syslog(LOG_ERR, "got signal %d", signo); 66689935Syar dologout(1); 66789935Syar} 66889935Syar 66925283Sdavidn#ifdef VIRTUAL_HOSTING 6701592Srgrimes/* 67125283Sdavidn * read in virtual host tables (if they exist) 67225283Sdavidn */ 67325283Sdavidn 67425283Sdavidnstatic void 675156156Sumeinithosts(int family) 67625283Sdavidn{ 677100182Syar int insert; 67899877Syar size_t len; 67925283Sdavidn FILE *fp; 68099877Syar char *cp, *mp, *line; 68199877Syar char *hostname; 682100182Syar char *vhost, *anonuser, *statfile, *welcome, *loginmsg; 68325283Sdavidn struct ftphost *hrp, *lhrp; 68456668Sshin struct addrinfo hints, *res, *ai; 68525283Sdavidn 68625283Sdavidn /* 68725283Sdavidn * Fill in the default host information 68825283Sdavidn */ 68999877Syar if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 69076096Smarkm fatalerror("Ran out of memory."); 691137983Syar if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 69299877Syar hostname[0] = '\0'; 69399877Syar hostname[MAXHOSTNAMELEN - 1] = '\0'; 69499877Syar if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 69599877Syar fatalerror("Ran out of memory."); 69699877Syar hrp->hostname = hostname; 69757124Sshin hrp->hostinfo = NULL; 69856668Sshin 69956668Sshin memset(&hints, 0, sizeof(hints)); 700156156Sume hints.ai_flags = AI_PASSIVE; 701156156Sume hints.ai_family = family; 702156156Sume hints.ai_socktype = SOCK_STREAM; 703102183Syar if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) 70457124Sshin hrp->hostinfo = res; 70525283Sdavidn hrp->statfile = _PATH_FTPDSTATFILE; 70625283Sdavidn hrp->welcome = _PATH_FTPWELCOME; 70725283Sdavidn hrp->loginmsg = _PATH_FTPLOGINMESG; 70825283Sdavidn hrp->anonuser = "ftp"; 70925283Sdavidn hrp->next = NULL; 71025283Sdavidn thishost = firsthost = lhrp = hrp; 71125283Sdavidn if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { 712102474Syar int addrsize, gothost; 71356668Sshin void *addr; 71456668Sshin struct hostent *hp; 71556668Sshin 71699877Syar while ((line = fgetln(fp, &len)) != NULL) { 71756668Sshin int i, hp_error; 71825283Sdavidn 71999877Syar /* skip comments */ 72099877Syar if (line[0] == '#') 72125283Sdavidn continue; 72299877Syar if (line[len - 1] == '\n') { 72399877Syar line[len - 1] = '\0'; 72499877Syar mp = NULL; 72599877Syar } else { 72699877Syar if ((mp = malloc(len + 1)) == NULL) 72799877Syar fatalerror("Ran out of memory."); 72899877Syar memcpy(mp, line, len); 72999877Syar mp[len] = '\0'; 73099877Syar line = mp; 73125283Sdavidn } 73225283Sdavidn cp = strtok(line, " \t"); 73399877Syar /* skip empty lines */ 73499877Syar if (cp == NULL) 73599877Syar goto nextline; 736100182Syar vhost = cp; 73756668Sshin 738100182Syar /* set defaults */ 739100182Syar anonuser = "ftp"; 740100182Syar statfile = _PATH_FTPDSTATFILE; 741100182Syar welcome = _PATH_FTPWELCOME; 742100182Syar loginmsg = _PATH_FTPLOGINMESG; 743100182Syar 744100182Syar /* 745100182Syar * Preparse the line so we can use its info 746100182Syar * for all the addresses associated with 747100182Syar * the virtual host name. 748100182Syar * Field 0, the virtual host name, is special: 749100182Syar * it's already parsed off and will be strdup'ed 750100182Syar * later, after we know its canonical form. 751100182Syar */ 752100182Syar for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) 753100182Syar if (*cp != '-' && (cp = strdup(cp))) 754100182Syar switch (i) { 755100182Syar case 1: /* anon user permissions */ 756100182Syar anonuser = cp; 757100182Syar break; 758100182Syar case 2: /* statistics file */ 759100182Syar statfile = cp; 760100182Syar break; 761100182Syar case 3: /* welcome message */ 762100182Syar welcome = cp; 763100182Syar break; 764100182Syar case 4: /* login message */ 765100182Syar loginmsg = cp; 766100182Syar break; 767100182Syar default: /* programming error */ 768100182Syar abort(); 769100182Syar /* NOTREACHED */ 770100182Syar } 771100182Syar 77256668Sshin hints.ai_flags = AI_PASSIVE; 773156156Sume hints.ai_family = family; 774156156Sume hints.ai_socktype = SOCK_STREAM; 775102183Syar if (getaddrinfo(vhost, NULL, &hints, &res) != 0) 77699877Syar goto nextline; 77756668Sshin for (ai = res; ai != NULL && ai->ai_addr != NULL; 77862100Sdavidn ai = ai->ai_next) { 77956668Sshin 78062100Sdavidn gothost = 0; 78125283Sdavidn for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { 78257124Sshin struct addrinfo *hi; 78357124Sshin 78457124Sshin for (hi = hrp->hostinfo; hi != NULL; 78557124Sshin hi = hi->ai_next) 78657124Sshin if (hi->ai_addrlen == ai->ai_addrlen && 78757124Sshin memcmp(hi->ai_addr, 78857124Sshin ai->ai_addr, 78962100Sdavidn ai->ai_addr->sa_len) == 0) { 79062100Sdavidn gothost++; 79157124Sshin break; 792100183Syar } 79362100Sdavidn if (gothost) 79462100Sdavidn break; 79525283Sdavidn } 79625283Sdavidn if (hrp == NULL) { 79725283Sdavidn if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 79899877Syar goto nextline; 799102183Syar hrp->hostname = NULL; 800100182Syar insert = 1; 801102473Syar } else { 802106754Syar if (hrp->hostinfo && hrp->hostinfo != res) 803102473Syar freeaddrinfo(hrp->hostinfo); 804100182Syar insert = 0; /* host already in the chain */ 805102473Syar } 80657124Sshin hrp->hostinfo = res; 80757124Sshin 80825283Sdavidn /* 80925283Sdavidn * determine hostname to use. 81056668Sshin * force defined name if there is a valid alias 81125283Sdavidn * otherwise fallback to primary hostname 81225283Sdavidn */ 81356668Sshin /* XXX: getaddrinfo() can't do alias check */ 81457124Sshin switch(hrp->hostinfo->ai_family) { 81556668Sshin case AF_INET: 816100259Syar addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; 817100259Syar addrsize = sizeof(struct in_addr); 81856668Sshin break; 81956668Sshin case AF_INET6: 820100259Syar addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; 821100259Syar addrsize = sizeof(struct in6_addr); 82256668Sshin break; 82356668Sshin default: 82456668Sshin /* should not reach here */ 825102473Syar freeaddrinfo(hrp->hostinfo); 826102473Syar if (insert) 827102473Syar free(hrp); /*not in chain, can free*/ 828102473Syar else 829102473Syar hrp->hostinfo = NULL; /*mark as blank*/ 83099877Syar goto nextline; 83156668Sshin /* NOTREACHED */ 83256668Sshin } 833100612Syar if ((hp = getipnodebyaddr(addr, addrsize, 83457124Sshin hrp->hostinfo->ai_family, 83556668Sshin &hp_error)) != NULL) { 836100182Syar if (strcmp(vhost, hp->h_name) != 0) { 83725283Sdavidn if (hp->h_aliases == NULL) 838100182Syar vhost = hp->h_name; 83925283Sdavidn else { 84025283Sdavidn i = 0; 84125283Sdavidn while (hp->h_aliases[i] && 842100182Syar strcmp(vhost, hp->h_aliases[i]) != 0) 84325283Sdavidn ++i; 84425283Sdavidn if (hp->h_aliases[i] == NULL) 845100182Syar vhost = hp->h_name; 84625283Sdavidn } 84725283Sdavidn } 84825283Sdavidn } 849102183Syar if (hrp->hostname && 850102183Syar strcmp(hrp->hostname, vhost) != 0) { 851102183Syar free(hrp->hostname); 852102183Syar hrp->hostname = NULL; 853102183Syar } 854102183Syar if (hrp->hostname == NULL && 855102473Syar (hrp->hostname = strdup(vhost)) == NULL) { 856102473Syar freeaddrinfo(hrp->hostinfo); 857102473Syar hrp->hostinfo = NULL; /* mark as blank */ 858102473Syar if (hp) 859102473Syar freehostent(hp); 860100182Syar goto nextline; 861102473Syar } 862100182Syar hrp->anonuser = anonuser; 863100182Syar hrp->statfile = statfile; 864100182Syar hrp->welcome = welcome; 865100182Syar hrp->loginmsg = loginmsg; 866100182Syar if (insert) { 867100182Syar hrp->next = NULL; 868100182Syar lhrp->next = hrp; 869100182Syar lhrp = hrp; 870100182Syar } 871100263Syar if (hp) 872100263Syar freehostent(hp); 87356668Sshin } 87499877Syarnextline: 87599877Syar if (mp) 87699877Syar free(mp); 87725283Sdavidn } 87825283Sdavidn (void) fclose(fp); 87925283Sdavidn } 88025283Sdavidn} 88125283Sdavidn 88225283Sdavidnstatic void 88390148Simpselecthost(union sockunion *su) 88425283Sdavidn{ 88525283Sdavidn struct ftphost *hrp; 88656668Sshin u_int16_t port; 88756668Sshin#ifdef INET6 88856668Sshin struct in6_addr *mapped_in6 = NULL; 88956668Sshin#endif 89057124Sshin struct addrinfo *hi; 89125283Sdavidn 89256668Sshin#ifdef INET6 89356668Sshin /* 89456668Sshin * XXX IPv4 mapped IPv6 addr consideraton, 89556668Sshin * specified in rfc2373. 89656668Sshin */ 89756668Sshin if (su->su_family == AF_INET6 && 89856668Sshin IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) 89956668Sshin mapped_in6 = &su->su_sin6.sin6_addr; 90056668Sshin#endif 90156668Sshin 90225283Sdavidn hrp = thishost = firsthost; /* default */ 90356668Sshin port = su->su_port; 90456668Sshin su->su_port = 0; 90525283Sdavidn while (hrp != NULL) { 90662100Sdavidn for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { 90757124Sshin if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { 90825283Sdavidn thishost = hrp; 909137987Syar goto found; 91025283Sdavidn } 91156668Sshin#ifdef INET6 91256668Sshin /* XXX IPv4 mapped IPv6 addr consideraton */ 91357124Sshin if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && 91456668Sshin (memcmp(&mapped_in6->s6_addr[12], 91557124Sshin &((struct sockaddr_in *)hi->ai_addr)->sin_addr, 91656668Sshin sizeof(struct in_addr)) == 0)) { 91756668Sshin thishost = hrp; 918137987Syar goto found; 91956668Sshin } 92056668Sshin#endif 92162100Sdavidn } 92262100Sdavidn hrp = hrp->next; 92325283Sdavidn } 924137987Syarfound: 92556668Sshin su->su_port = port; 92625283Sdavidn /* setup static variables as appropriate */ 92725283Sdavidn hostname = thishost->hostname; 92825283Sdavidn ftpuser = thishost->anonuser; 92925283Sdavidn} 93025283Sdavidn#endif 93125283Sdavidn 93225283Sdavidn/* 9331592Srgrimes * Helper function for sgetpwnam(). 9341592Srgrimes */ 9351592Srgrimesstatic char * 93690148Simpsgetsave(char *s) 9371592Srgrimes{ 938137659Syar char *new = malloc(strlen(s) + 1); 9391592Srgrimes 9401592Srgrimes if (new == NULL) { 941137852Syar reply(421, "Ran out of memory."); 9421592Srgrimes dologout(1); 9431592Srgrimes /* NOTREACHED */ 9441592Srgrimes } 9451592Srgrimes (void) strcpy(new, s); 9461592Srgrimes return (new); 9471592Srgrimes} 9481592Srgrimes 9491592Srgrimes/* 9501592Srgrimes * Save the result of a getpwnam. Used for USER command, since 9511592Srgrimes * the data returned must not be clobbered by any other command 9521592Srgrimes * (e.g., globbing). 953137076Syar * NB: The data returned by sgetpwnam() will remain valid until 954137076Syar * the next call to this function. Its difference from getpwnam() 955137076Syar * is that sgetpwnam() is known to be called from ftpd code only. 9561592Srgrimes */ 9571592Srgrimesstatic struct passwd * 95890148Simpsgetpwnam(char *name) 9591592Srgrimes{ 9601592Srgrimes static struct passwd save; 9611592Srgrimes struct passwd *p; 9621592Srgrimes 9631592Srgrimes if ((p = getpwnam(name)) == NULL) 9641592Srgrimes return (p); 9651592Srgrimes if (save.pw_name) { 9661592Srgrimes free(save.pw_name); 9671592Srgrimes free(save.pw_passwd); 968262284Sbrueffer free(save.pw_class); 9691592Srgrimes free(save.pw_gecos); 9701592Srgrimes free(save.pw_dir); 9711592Srgrimes free(save.pw_shell); 9721592Srgrimes } 9731592Srgrimes save = *p; 9741592Srgrimes save.pw_name = sgetsave(p->pw_name); 9751592Srgrimes save.pw_passwd = sgetsave(p->pw_passwd); 976262284Sbrueffer save.pw_class = sgetsave(p->pw_class); 9771592Srgrimes save.pw_gecos = sgetsave(p->pw_gecos); 9781592Srgrimes save.pw_dir = sgetsave(p->pw_dir); 9791592Srgrimes save.pw_shell = sgetsave(p->pw_shell); 9801592Srgrimes return (&save); 9811592Srgrimes} 9821592Srgrimes 9831592Srgrimesstatic int login_attempts; /* number of failed login attempts */ 9841592Srgrimesstatic int askpasswd; /* had user command, ask for passwd */ 98564778Ssheldonhstatic char curname[MAXLOGNAME]; /* current USER name */ 9861592Srgrimes 9871592Srgrimes/* 9881592Srgrimes * USER command. 9891592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable; 9901592Srgrimes * sets askpasswd if a PASS command is expected. If logged in previously, 9911592Srgrimes * need to reset state. If name is "ftp" or "anonymous", the name is not in 9921592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 9931592Srgrimes * If account doesn't exist, ask for passwd anyway. Otherwise, check user 9941592Srgrimes * requesting login privileges. Disallow anyone who does not have a standard 9951592Srgrimes * shell as returned by getusershell(). Disallow anyone mentioned in the file 9961592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 9971592Srgrimes */ 9981592Srgrimesvoid 99990148Simpuser(char *name) 10001592Srgrimes{ 1001216932Scsjp int ecode; 10021592Srgrimes char *cp, *shell; 10031592Srgrimes 10041592Srgrimes if (logged_in) { 10051592Srgrimes if (guest) { 10061592Srgrimes reply(530, "Can't change user from guest login."); 10071592Srgrimes return; 100817435Spst } else if (dochroot) { 100917435Spst reply(530, "Can't change user from chroot user."); 101017435Spst return; 10111592Srgrimes } 10121592Srgrimes end_login(); 10131592Srgrimes } 10141592Srgrimes 10151592Srgrimes guest = 0; 1016130428Sobrien#ifdef VIRTUAL_HOSTING 1017130428Sobrien pw = sgetpwnam(thishost->anonuser); 1018130428Sobrien#else 1019130428Sobrien pw = sgetpwnam("ftp"); 1020130428Sobrien#endif 10211592Srgrimes if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 1022216932Scsjp if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) || 1023216932Scsjp (ecode != 0 && ecode != ENOENT)) 10241592Srgrimes reply(530, "User %s access denied.", name); 1025216932Scsjp else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) || 1026216932Scsjp (ecode != 0 && ecode != ENOENT)) 1027216932Scsjp reply(530, "User %s access denied.", name); 1028130428Sobrien else if (pw != NULL) { 10291592Srgrimes guest = 1; 10301592Srgrimes askpasswd = 1; 10311592Srgrimes reply(331, 10323938Spst "Guest login ok, send your email address as password."); 10331592Srgrimes } else 10341592Srgrimes reply(530, "User %s unknown.", name); 10351592Srgrimes if (!askpasswd && logging) 10361592Srgrimes syslog(LOG_NOTICE, 10371592Srgrimes "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 10381592Srgrimes return; 10391592Srgrimes } 104020042Storstenb if (anon_only != 0) { 104120042Storstenb reply(530, "Sorry, only anonymous ftp allowed."); 104220042Storstenb return; 104320042Storstenb } 104420042Storstenb 104517478Smarkm if ((pw = sgetpwnam(name))) { 10461592Srgrimes if ((shell = pw->pw_shell) == NULL || *shell == 0) 10471592Srgrimes shell = _PATH_BSHELL; 1048124687Scharnier setusershell(); 10491592Srgrimes while ((cp = getusershell()) != NULL) 10501592Srgrimes if (strcmp(cp, shell) == 0) 10511592Srgrimes break; 10521592Srgrimes endusershell(); 10531592Srgrimes 1054216932Scsjp if (cp == NULL || 1055216932Scsjp (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) || 1056216932Scsjp (ecode != 0 && ecode != ENOENT))) { 10571592Srgrimes reply(530, "User %s access denied.", name); 10581592Srgrimes if (logging) 10591592Srgrimes syslog(LOG_NOTICE, 10601592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 10611592Srgrimes remotehost, name); 1062137811Syar pw = NULL; 10631592Srgrimes return; 10641592Srgrimes } 10651592Srgrimes } 10661592Srgrimes if (logging) 1067327019Sdelphij strlcpy(curname, name, sizeof(curname)); 106888763Sache 106988763Sache pwok = 0; 107079469Smarkm#ifdef USE_PAM 107179469Smarkm /* XXX Kluge! The conversation mechanism needs to be fixed. */ 107288763Sache#endif 107388763Sache if (opiechallenge(&opiedata, name, opieprompt) == 0) { 107488763Sache pwok = (pw != NULL) && 107588763Sache opieaccessfile(remotehost) && 107688763Sache opiealways(pw->pw_dir); 107788763Sache reply(331, "Response to %s %s for %s.", 107888763Sache opieprompt, pwok ? "requested" : "required", name); 107988763Sache } else { 108088763Sache pwok = 1; 108184146Sache reply(331, "Password required for %s.", name); 108288763Sache } 10831592Srgrimes askpasswd = 1; 10841592Srgrimes /* 10851592Srgrimes * Delay before reading passwd after first failed 10861592Srgrimes * attempt to slow down passwd-guessing programs. 10871592Srgrimes */ 10881592Srgrimes if (login_attempts) 1089137659Syar sleep(login_attempts); 10901592Srgrimes} 10911592Srgrimes 10921592Srgrimes/* 1093109893Syar * Check if a user is in the file "fname", 1094109893Syar * return a pointer to a malloc'd string with the rest 1095109893Syar * of the matching line in "residue" if not NULL. 10961592Srgrimes */ 10971592Srgrimesstatic int 1098216932Scsjpcheckuser(char *fname, char *name, int pwset, char **residue, int *ecode) 10991592Srgrimes{ 11001592Srgrimes FILE *fd; 11011592Srgrimes int found = 0; 110299877Syar size_t len; 110399877Syar char *line, *mp, *p; 11041592Srgrimes 1105216932Scsjp if (ecode != NULL) 1106216932Scsjp *ecode = 0; 110717435Spst if ((fd = fopen(fname, "r")) != NULL) { 110899877Syar while (!found && (line = fgetln(fd, &len)) != NULL) { 110999877Syar /* skip comments */ 111099877Syar if (line[0] == '#') 111199877Syar continue; 111299877Syar if (line[len - 1] == '\n') { 111399877Syar line[len - 1] = '\0'; 111499877Syar mp = NULL; 111599877Syar } else { 111699877Syar if ((mp = malloc(len + 1)) == NULL) 111799877Syar fatalerror("Ran out of memory."); 111899877Syar memcpy(mp, line, len); 111999877Syar mp[len] = '\0'; 112099877Syar line = mp; 112199877Syar } 112299877Syar /* avoid possible leading and trailing whitespace */ 112399877Syar p = strtok(line, " \t"); 112499877Syar /* skip empty lines */ 112599877Syar if (p == NULL) 112699877Syar goto nextline; 112799877Syar /* 112899877Syar * if first chr is '@', check group membership 112999877Syar */ 113099877Syar if (p[0] == '@') { 113199877Syar int i = 0; 113299877Syar struct group *grp; 113399877Syar 1134109893Syar if (p[1] == '\0') /* single @ matches anyone */ 113599877Syar found = 1; 1136109893Syar else { 1137109893Syar if ((grp = getgrnam(p+1)) == NULL) 1138109893Syar goto nextline; 1139109893Syar /* 1140109893Syar * Check user's default group 1141109893Syar */ 1142109893Syar if (pwset && grp->gr_gid == pw->pw_gid) 1143109893Syar found = 1; 1144109893Syar /* 1145109893Syar * Check supplementary groups 1146109893Syar */ 1147109893Syar while (!found && grp->gr_mem[i]) 1148109893Syar found = strcmp(name, 1149109893Syar grp->gr_mem[i++]) 1150109893Syar == 0; 1151109893Syar } 11521592Srgrimes } 115399877Syar /* 115499877Syar * Otherwise, just check for username match 115599877Syar */ 115699877Syar else 115799877Syar found = strcmp(p, name) == 0; 1158109893Syar /* 1159109893Syar * Save the rest of line to "residue" if matched 1160109893Syar */ 1161109893Syar if (found && residue) { 1162109938Syar if ((p = strtok(NULL, "")) != NULL) 1163109938Syar p += strspn(p, " \t"); 1164109938Syar if (p && *p) { 1165109893Syar if ((*residue = strdup(p)) == NULL) 1166109893Syar fatalerror("Ran out of memory."); 1167109893Syar } else 1168109893Syar *residue = NULL; 1169109893Syar } 117099877Syarnextline: 117199877Syar if (mp) 117299877Syar free(mp); 117399877Syar } 11741592Srgrimes (void) fclose(fd); 1175216932Scsjp } else if (ecode != NULL) 1176216932Scsjp *ecode = errno; 11771592Srgrimes return (found); 11781592Srgrimes} 11791592Srgrimes 11801592Srgrimes/* 11811592Srgrimes * Terminate login as previous user, if any, resetting state; 11821592Srgrimes * used when USER command is given or login fails. 11831592Srgrimes */ 11841592Srgrimesstatic void 118590148Simpend_login(void) 11861592Srgrimes{ 118774874Smarkm#ifdef USE_PAM 118874874Smarkm int e; 118974874Smarkm#endif 11901592Srgrimes 1191132893Syar (void) seteuid(0); 119225101Sdavidn#ifdef LOGIN_CAP 1193223434Strasz setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | 1194223434Strasz LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | 1195223434Strasz LOGIN_SETENV)); 119625101Sdavidn#endif 1197325472Seugen if (logged_in && dowtmp) 1198325472Seugen ftpd_logwtmp(wtmpid, NULL, NULL); 1199325472Seugen pw = NULL; 120074874Smarkm#ifdef USE_PAM 1201137078Syar if (pamh) { 1202137078Syar if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 1203137078Syar syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 1204137078Syar if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 1205137078Syar syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 1206137078Syar if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 1207137078Syar syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 1208137078Syar pamh = NULL; 1209137078Syar } 121074874Smarkm#endif 12111592Srgrimes logged_in = 0; 12121592Srgrimes guest = 0; 121317435Spst dochroot = 0; 12141592Srgrimes} 12151592Srgrimes 121674874Smarkm#ifdef USE_PAM 121751433Smarkm 121851433Smarkm/* 121951433Smarkm * the following code is stolen from imap-uw PAM authentication module and 122051433Smarkm * login.c 122151433Smarkm */ 122251433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL) 122351433Smarkm 122451433Smarkmstruct cred_t { 122551433Smarkm const char *uname; /* user name */ 122651433Smarkm const char *pass; /* password */ 122751433Smarkm}; 122851433Smarkmtypedef struct cred_t cred_t; 122951433Smarkm 123051433Smarkmstatic int 123151433Smarkmauth_conv(int num_msg, const struct pam_message **msg, 123251433Smarkm struct pam_response **resp, void *appdata) 123351433Smarkm{ 123451433Smarkm int i; 123551433Smarkm cred_t *cred = (cred_t *) appdata; 123691244Sdes struct pam_response *reply; 123751433Smarkm 123891244Sdes reply = calloc(num_msg, sizeof *reply); 123991244Sdes if (reply == NULL) 124091244Sdes return PAM_BUF_ERR; 124191244Sdes 124251433Smarkm for (i = 0; i < num_msg; i++) { 124351433Smarkm switch (msg[i]->msg_style) { 124451433Smarkm case PAM_PROMPT_ECHO_ON: /* assume want user name */ 124551433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 124651433Smarkm reply[i].resp = COPY_STRING(cred->uname); 124751433Smarkm /* PAM frees resp. */ 124851433Smarkm break; 124951433Smarkm case PAM_PROMPT_ECHO_OFF: /* assume want password */ 125051433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 125151433Smarkm reply[i].resp = COPY_STRING(cred->pass); 125251433Smarkm /* PAM frees resp. */ 125351433Smarkm break; 125451433Smarkm case PAM_TEXT_INFO: 125551433Smarkm case PAM_ERROR_MSG: 125651433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 125751433Smarkm reply[i].resp = NULL; 125851433Smarkm break; 125951433Smarkm default: /* unknown message style */ 126051433Smarkm free(reply); 126151433Smarkm return PAM_CONV_ERR; 126251433Smarkm } 126351433Smarkm } 126451433Smarkm 126551433Smarkm *resp = reply; 126651433Smarkm return PAM_SUCCESS; 126751433Smarkm} 126851433Smarkm 126951433Smarkm/* 127051433Smarkm * Attempt to authenticate the user using PAM. Returns 0 if the user is 127151433Smarkm * authenticated, or 1 if not authenticated. If some sort of PAM system 127251433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 127351433Smarkm * function returns -1. This can be used as an indication that we should 127451433Smarkm * fall back to a different authentication mechanism. 127551433Smarkm */ 127651433Smarkmstatic int 127751433Smarkmauth_pam(struct passwd **ppw, const char *pass) 127851433Smarkm{ 127951433Smarkm const char *tmpl_user; 128051433Smarkm const void *item; 128151433Smarkm int rval; 128251433Smarkm int e; 128351433Smarkm cred_t auth_cred = { (*ppw)->pw_name, pass }; 128451433Smarkm struct pam_conv conv = { &auth_conv, &auth_cred }; 128551433Smarkm 128651433Smarkm e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 128751433Smarkm if (e != PAM_SUCCESS) { 1288137108Syar /* 1289137108Syar * In OpenPAM, it's OK to pass NULL to pam_strerror() 1290137108Syar * if context creation has failed in the first place. 1291137108Syar */ 1292137108Syar syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); 129351433Smarkm return -1; 129451433Smarkm } 129551433Smarkm 129667007Sguido e = pam_set_item(pamh, PAM_RHOST, remotehost); 129767007Sguido if (e != PAM_SUCCESS) { 129867007Sguido syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 129967007Sguido pam_strerror(pamh, e)); 1300137078Syar if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 1301137078Syar syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 1302137078Syar } 1303137078Syar pamh = NULL; 130467007Sguido return -1; 130567007Sguido } 130667007Sguido 130751433Smarkm e = pam_authenticate(pamh, 0); 130851433Smarkm switch (e) { 130951433Smarkm case PAM_SUCCESS: 131051433Smarkm /* 131151433Smarkm * With PAM we support the concept of a "template" 131251433Smarkm * user. The user enters a login name which is 131351433Smarkm * authenticated by PAM, usually via a remote service 131451433Smarkm * such as RADIUS or TACACS+. If authentication 131551433Smarkm * succeeds, a different but related "template" name 131651433Smarkm * is used for setting the credentials, shell, and 131751433Smarkm * home directory. The name the user enters need only 131851433Smarkm * exist on the remote authentication server, but the 131951433Smarkm * template name must be present in the local password 132051433Smarkm * database. 132151433Smarkm * 132251433Smarkm * This is supported by two various mechanisms in the 132351433Smarkm * individual modules. However, from the application's 132451433Smarkm * point of view, the template user is always passed 132551433Smarkm * back as a changed value of the PAM_USER item. 132651433Smarkm */ 132751433Smarkm if ((e = pam_get_item(pamh, PAM_USER, &item)) == 132851433Smarkm PAM_SUCCESS) { 132951433Smarkm tmpl_user = (const char *) item; 133051433Smarkm if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 133151433Smarkm *ppw = getpwnam(tmpl_user); 133251433Smarkm } else 133351433Smarkm syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 133451433Smarkm pam_strerror(pamh, e)); 133551433Smarkm rval = 0; 133651433Smarkm break; 133751433Smarkm 133851433Smarkm case PAM_AUTH_ERR: 133951433Smarkm case PAM_USER_UNKNOWN: 134051433Smarkm case PAM_MAXTRIES: 134151433Smarkm rval = 1; 134251433Smarkm break; 134351433Smarkm 134451433Smarkm default: 134574874Smarkm syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 134651433Smarkm rval = -1; 134751433Smarkm break; 134851433Smarkm } 134951433Smarkm 135074874Smarkm if (rval == 0) { 135174874Smarkm e = pam_acct_mgmt(pamh, 0); 1352137986Syar if (e != PAM_SUCCESS) { 1353137986Syar syslog(LOG_ERR, "pam_acct_mgmt: %s", 1354137986Syar pam_strerror(pamh, e)); 135574874Smarkm rval = 1; 135674874Smarkm } 135751433Smarkm } 135874874Smarkm 135974874Smarkm if (rval != 0) { 136074874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 136174874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 136274874Smarkm } 136374874Smarkm pamh = NULL; 136474874Smarkm } 136551433Smarkm return rval; 136651433Smarkm} 136751433Smarkm 136874874Smarkm#endif /* USE_PAM */ 136951433Smarkm 13701592Srgrimesvoid 137190148Simppass(char *passwd) 13721592Srgrimes{ 1373216932Scsjp int rval, ecode; 13741592Srgrimes FILE *fd; 137525101Sdavidn#ifdef LOGIN_CAP 137625101Sdavidn login_cap_t *lc = NULL; 137725101Sdavidn#endif 137874874Smarkm#ifdef USE_PAM 137974874Smarkm int e; 138074874Smarkm#endif 1381109939Syar char *residue = NULL; 138288763Sache char *xpasswd; 13831592Srgrimes 13841592Srgrimes if (logged_in || askpasswd == 0) { 13851592Srgrimes reply(503, "Login with USER first."); 13861592Srgrimes return; 13871592Srgrimes } 13881592Srgrimes askpasswd = 0; 13891592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 139017435Spst if (pw == NULL) { 139117435Spst rval = 1; /* failure below */ 139217435Spst goto skip; 139317435Spst } 139474874Smarkm#ifdef USE_PAM 139551433Smarkm rval = auth_pam(&pw, passwd); 139689622Sache if (rval >= 0) { 139789622Sache opieunlock(); 139817435Spst goto skip; 139989622Sache } 140089622Sache#endif 140188763Sache if (opieverify(&opiedata, passwd) == 0) 140288763Sache xpasswd = pw->pw_passwd; 140389622Sache else if (pwok) { 140488763Sache xpasswd = crypt(passwd, pw->pw_passwd); 140589622Sache if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 140689622Sache xpasswd = ":"; 140789622Sache } else { 140888763Sache rval = 1; 140988763Sache goto skip; 141088763Sache } 141188763Sache rval = strcmp(pw->pw_passwd, xpasswd); 141289622Sache if (pw->pw_expire && time(NULL) >= pw->pw_expire) 141317435Spst rval = 1; /* failure */ 141417435Spstskip: 141517435Spst /* 141617435Spst * If rval == 1, the user failed the authentication check 141751433Smarkm * above. If rval == 0, either PAM or local authentication 141817435Spst * succeeded. 141917435Spst */ 142017435Spst if (rval) { 14211592Srgrimes reply(530, "Login incorrect."); 1422110691Syar if (logging) { 14231592Srgrimes syslog(LOG_NOTICE, 1424110691Syar "FTP LOGIN FAILED FROM %s", 1425110691Syar remotehost); 1426110691Syar syslog(LOG_AUTHPRIV | LOG_NOTICE, 14271592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 14281592Srgrimes remotehost, curname); 1429110691Syar } 14301592Srgrimes pw = NULL; 14311592Srgrimes if (login_attempts++ >= 5) { 14321592Srgrimes syslog(LOG_NOTICE, 14331592Srgrimes "repeated login failures from %s", 14341592Srgrimes remotehost); 14351592Srgrimes exit(0); 14361592Srgrimes } 14371592Srgrimes return; 14381592Srgrimes } 14391592Srgrimes } 14401592Srgrimes login_attempts = 0; /* this time successful */ 1441132894Syar if (setegid(pw->pw_gid) < 0) { 14421592Srgrimes reply(550, "Can't set gid."); 14431592Srgrimes return; 14441592Srgrimes } 144525101Sdavidn /* May be overridden by login.conf */ 144625101Sdavidn (void) umask(defumask); 144725101Sdavidn#ifdef LOGIN_CAP 144825674Sdavidn if ((lc = login_getpwclass(pw)) != NULL) { 1449137983Syar char remote_ip[NI_MAXHOST]; 145025101Sdavidn 1451137983Syar if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 145256668Sshin remote_ip, sizeof(remote_ip) - 1, NULL, 0, 1453137983Syar NI_NUMERICHOST)) 1454137983Syar *remote_ip = 0; 145525101Sdavidn remote_ip[sizeof(remote_ip) - 1] = 0; 145625101Sdavidn if (!auth_hostok(lc, remotehost, remote_ip)) { 145725101Sdavidn syslog(LOG_INFO|LOG_AUTH, 145825101Sdavidn "FTP LOGIN FAILED (HOST) as %s: permission denied.", 145925101Sdavidn pw->pw_name); 1460137849Syar reply(530, "Permission denied."); 146125101Sdavidn pw = NULL; 146225101Sdavidn return; 146325101Sdavidn } 146425101Sdavidn if (!auth_timeok(lc, time(NULL))) { 1465137849Syar reply(530, "Login not available right now."); 146625101Sdavidn pw = NULL; 146725101Sdavidn return; 146825101Sdavidn } 146925101Sdavidn } 1470223434Strasz setusercontext(lc, pw, 0, LOGIN_SETALL & 1471325472Seugen ~(LOGIN_SETRESOURCES | LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV)); 147225101Sdavidn#else 147340310Sdes setlogin(pw->pw_name); 14741592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 147525101Sdavidn#endif 14761592Srgrimes 147774874Smarkm#ifdef USE_PAM 147874874Smarkm if (pamh) { 147974874Smarkm if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 148074874Smarkm syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 148174874Smarkm } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 148274874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 148374874Smarkm } 148474874Smarkm } 148574874Smarkm#endif 148674874Smarkm 1487202209Sed dochroot = 1488216932Scsjp checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode) 1489202209Sed#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 1490202209Sed || login_getcapbool(lc, "ftp-chroot", 0) 1491202209Sed#endif 1492202209Sed ; 1493216932Scsjp /* 1494216932Scsjp * It is possible that checkuser() failed to open the chroot file. 1495216932Scsjp * If this is the case, report that logins are un-available, since we 1496216932Scsjp * have no way of checking whether or not the user should be chrooted. 1497216932Scsjp * We ignore ENOENT since it is not required that this file be present. 1498216932Scsjp */ 1499216932Scsjp if (ecode != 0 && ecode != ENOENT) { 1500216932Scsjp reply(530, "Login not available right now."); 1501216932Scsjp return; 1502216932Scsjp } 1503202209Sed chrootdir = NULL; 1504202209Sed 1505202604Sed /* Disable wtmp logging when chrooting. */ 1506202604Sed if (dochroot || guest) 1507202604Sed dowtmp = 0; 1508202604Sed if (dowtmp) 1509202209Sed ftpd_logwtmp(wtmpid, pw->pw_name, 1510102311Syar (struct sockaddr *)&his_addr); 15111592Srgrimes logged_in = 1; 15121592Srgrimes 1513325472Seugen#ifdef LOGIN_CAP 1514325472Seugen setusercontext(lc, pw, 0, LOGIN_SETRESOURCES); 1515325472Seugen#endif 1516325472Seugen 151717435Spst if (guest && stats && statfd < 0) 151825283Sdavidn#ifdef VIRTUAL_HOSTING 1519130428Sobrien statfd = open(thishost->statfile, O_WRONLY|O_APPEND); 152025283Sdavidn#else 1521130428Sobrien statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); 152225283Sdavidn#endif 1523130428Sobrien if (statfd < 0) 15246740Sguido stats = 0; 15256740Sguido 1526110036Syar /* 1527110036Syar * For a chrooted local user, 1528110036Syar * a) see whether ftpchroot(5) specifies a chroot directory, 1529110036Syar * b) extract the directory pathname from the line, 1530110036Syar * c) expand it to the absolute pathname if necessary. 1531110036Syar */ 1532110036Syar if (dochroot && residue && 1533117349Syar (chrootdir = strtok(residue, " \t")) != NULL) { 1534117349Syar if (chrootdir[0] != '/') 1535117349Syar asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); 1536117349Syar else 1537137862Syar chrootdir = strdup(chrootdir); /* make it permanent */ 1538110036Syar if (chrootdir == NULL) 1539110036Syar fatalerror("Ran out of memory."); 1540110036Syar } 1541110036Syar if (guest || dochroot) { 15421592Srgrimes /* 1543110036Syar * If no chroot directory set yet, use the login directory. 1544110036Syar * Copy it so it can be modified while pw->pw_dir stays intact. 15451592Srgrimes */ 1546110036Syar if (chrootdir == NULL && 1547110036Syar (chrootdir = strdup(pw->pw_dir)) == NULL) 1548110036Syar fatalerror("Ran out of memory."); 1549110036Syar /* 1550110036Syar * Check for the "/chroot/./home" syntax, 1551110036Syar * separate the chroot and home directory pathnames. 1552110036Syar */ 1553110036Syar if ((homedir = strstr(chrootdir, "/./")) != NULL) { 1554110036Syar *(homedir++) = '\0'; /* wipe '/' */ 1555110036Syar homedir++; /* skip '.' */ 1556110036Syar } else { 1557110036Syar /* 1558110036Syar * We MUST do a chdir() after the chroot. Otherwise 1559110036Syar * the old current directory will be accessible as "." 1560110036Syar * outside the new root! 1561110036Syar */ 1562110036Syar homedir = "/"; 1563109939Syar } 1564110036Syar /* 1565110036Syar * Finally, do chroot() 1566110036Syar */ 1567110036Syar if (chroot(chrootdir) < 0) { 156817435Spst reply(550, "Can't change root."); 156917435Spst goto bad; 157017435Spst } 1571228843Scperciva __FreeBSD_libc_enter_restricted_mode(); 1572110036Syar } else /* real user w/o chroot */ 1573110036Syar homedir = pw->pw_dir; 1574110036Syar /* 1575110036Syar * Set euid *before* doing chdir() so 1576110036Syar * a) the user won't be carried to a directory that he couldn't reach 1577110036Syar * on his own due to no permission to upper path components, 1578110036Syar * b) NFS mounted homedirs w/restrictive permissions will be accessible 1579110036Syar * (uid 0 has no root power over NFS if not mapped explicitly.) 1580110036Syar */ 1581132893Syar if (seteuid(pw->pw_uid) < 0) { 15821592Srgrimes reply(550, "Can't set uid."); 15831592Srgrimes goto bad; 15841592Srgrimes } 1585110036Syar if (chdir(homedir) < 0) { 1586110036Syar if (guest || dochroot) { 1587110036Syar reply(550, "Can't change to base directory."); 1588110036Syar goto bad; 1589110036Syar } else { 1590110036Syar if (chdir("/") < 0) { 1591110036Syar reply(550, "Root is inaccessible."); 1592110036Syar goto bad; 1593110036Syar } 1594137852Syar lreply(230, "No directory! Logging in with home=/."); 1595110036Syar } 1596110036Syar } 15978696Sdg 15981592Srgrimes /* 15991592Srgrimes * Display a login message, if it exists. 16001592Srgrimes * N.B. reply(230,) must follow the message. 16011592Srgrimes */ 160225283Sdavidn#ifdef VIRTUAL_HOSTING 1603130428Sobrien fd = fopen(thishost->loginmsg, "r"); 160425283Sdavidn#else 1605130428Sobrien fd = fopen(_PATH_FTPLOGINMESG, "r"); 160625283Sdavidn#endif 1607130428Sobrien if (fd != NULL) { 16081592Srgrimes char *cp, line[LINE_MAX]; 16091592Srgrimes 16101592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 16111592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 16121592Srgrimes *cp = '\0'; 16131592Srgrimes lreply(230, "%s", line); 16141592Srgrimes } 16151592Srgrimes (void) fflush(stdout); 16161592Srgrimes (void) fclose(fd); 16171592Srgrimes } 16181592Srgrimes if (guest) { 16196740Sguido if (ident != NULL) 16206740Sguido free(ident); 162117433Spst ident = strdup(passwd); 162217433Spst if (ident == NULL) 162376096Smarkm fatalerror("Ran out of memory."); 162417433Spst 16251592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 16261592Srgrimes#ifdef SETPROCTITLE 162725283Sdavidn#ifdef VIRTUAL_HOSTING 162825283Sdavidn if (thishost != firsthost) 162925283Sdavidn snprintf(proctitle, sizeof(proctitle), 163083308Smikeh "%s: anonymous(%s)/%s", remotehost, hostname, 163183308Smikeh passwd); 163225283Sdavidn else 163325283Sdavidn#endif 163425283Sdavidn snprintf(proctitle, sizeof(proctitle), 163583308Smikeh "%s: anonymous/%s", remotehost, passwd); 163613139Speter setproctitle("%s", proctitle); 16371592Srgrimes#endif /* SETPROCTITLE */ 16381592Srgrimes if (logging) 16391592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 16401592Srgrimes remotehost, passwd); 16411592Srgrimes } else { 164284841Syar if (dochroot) 164384841Syar reply(230, "User %s logged in, " 164484841Syar "access restrictions apply.", pw->pw_name); 164584841Syar else 164684841Syar reply(230, "User %s logged in.", pw->pw_name); 164725986Sdanny 16481592Srgrimes#ifdef SETPROCTITLE 16491592Srgrimes snprintf(proctitle, sizeof(proctitle), 165084842Syar "%s: user/%s", remotehost, pw->pw_name); 165113139Speter setproctitle("%s", proctitle); 16521592Srgrimes#endif /* SETPROCTITLE */ 16531592Srgrimes if (logging) 16541592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 16551592Srgrimes remotehost, pw->pw_name); 16561592Srgrimes } 1657140473Syar if (logging && (guest || dochroot)) 1658137985Syar syslog(LOG_INFO, "session root changed to %s", chrootdir); 165925101Sdavidn#ifdef LOGIN_CAP 166025101Sdavidn login_close(lc); 166125101Sdavidn#endif 1662110036Syar if (residue) 1663110036Syar free(residue); 16641592Srgrimes return; 16651592Srgrimesbad: 16661592Srgrimes /* Forget all about it... */ 166725101Sdavidn#ifdef LOGIN_CAP 166825101Sdavidn login_close(lc); 166925101Sdavidn#endif 1670110036Syar if (residue) 1671110036Syar free(residue); 16721592Srgrimes end_login(); 16731592Srgrimes} 16741592Srgrimes 16751592Srgrimesvoid 167690148Simpretrieve(char *cmd, char *name) 16771592Srgrimes{ 16781592Srgrimes FILE *fin, *dout; 16791592Srgrimes struct stat st; 168090148Simp int (*closefunc)(FILE *); 168136612Sjb time_t start; 1682300273Struckman char line[BUFSIZ]; 16831592Srgrimes 16841592Srgrimes if (cmd == 0) { 16851592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 16861592Srgrimes st.st_size = 0; 16871592Srgrimes } else { 1688300273Struckman (void) snprintf(line, sizeof(line), cmd, name); 1689300273Struckman name = line; 16901592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 16911592Srgrimes st.st_size = -1; 16921592Srgrimes st.st_blksize = BUFSIZ; 16931592Srgrimes } 16941592Srgrimes if (fin == NULL) { 16951592Srgrimes if (errno != 0) { 16961592Srgrimes perror_reply(550, name); 16971592Srgrimes if (cmd == 0) { 16981592Srgrimes LOGCMD("get", name); 16991592Srgrimes } 17001592Srgrimes } 17011592Srgrimes return; 17021592Srgrimes } 17031592Srgrimes byte_count = -1; 1704110144Syar if (cmd == 0) { 1705110144Syar if (fstat(fileno(fin), &st) < 0) { 1706110144Syar perror_reply(550, name); 1707110144Syar goto done; 1708110144Syar } 1709110144Syar if (!S_ISREG(st.st_mode)) { 1710125565Syar /* 1711125565Syar * Never sending a raw directory is a workaround 1712125565Syar * for buggy clients that will attempt to RETR 1713125565Syar * a directory before listing it, e.g., Mozilla. 1714125565Syar * Preventing a guest from getting irregular files 1715125565Syar * is a simple security measure. 1716125565Syar */ 1717125565Syar if (S_ISDIR(st.st_mode) || guest) { 1718110144Syar reply(550, "%s: not a plain file.", name); 1719110144Syar goto done; 1720110144Syar } 1721110144Syar st.st_size = -1; 1722110144Syar /* st.st_blksize is set for all descriptor types */ 1723110144Syar } 17241592Srgrimes } 17251592Srgrimes if (restart_point) { 17261592Srgrimes if (type == TYPE_A) { 17271592Srgrimes off_t i, n; 17281592Srgrimes int c; 17291592Srgrimes 17301592Srgrimes n = restart_point; 17311592Srgrimes i = 0; 17321592Srgrimes while (i++ < n) { 17331592Srgrimes if ((c=getc(fin)) == EOF) { 17341592Srgrimes perror_reply(550, name); 17351592Srgrimes goto done; 17361592Srgrimes } 17371592Srgrimes if (c == '\n') 17381592Srgrimes i++; 17391592Srgrimes } 17401592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 17411592Srgrimes perror_reply(550, name); 17421592Srgrimes goto done; 17431592Srgrimes } 17441592Srgrimes } 17451592Srgrimes dout = dataconn(name, st.st_size, "w"); 17461592Srgrimes if (dout == NULL) 17471592Srgrimes goto done; 17486740Sguido time(&start); 17498240Swollman send_data(fin, dout, st.st_blksize, st.st_size, 17508240Swollman restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 1751136929Syar if (cmd == 0 && guest && stats && byte_count > 0) 1752136929Syar logxfer(name, byte_count, start); 17531592Srgrimes (void) fclose(dout); 17541592Srgrimes data = -1; 17551592Srgrimes pdata = -1; 17561592Srgrimesdone: 17571592Srgrimes if (cmd == 0) 17581592Srgrimes LOGBYTES("get", name, byte_count); 17591592Srgrimes (*closefunc)(fin); 17601592Srgrimes} 17611592Srgrimes 17621592Srgrimesvoid 176390148Simpstore(char *name, char *mode, int unique) 17641592Srgrimes{ 1765101537Syar int fd; 17661592Srgrimes FILE *fout, *din; 176790148Simp int (*closefunc)(FILE *); 17681592Srgrimes 1769101537Syar if (*mode == 'a') { /* APPE */ 1770101537Syar if (unique) { 1771101537Syar /* Programming error */ 1772101537Syar syslog(LOG_ERR, "Internal: unique flag to APPE"); 1773101537Syar unique = 0; 1774101537Syar } 1775101537Syar if (guest && noguestmod) { 1776137852Syar reply(550, "Appending to existing file denied."); 1777101537Syar goto err; 1778101537Syar } 1779101537Syar restart_point = 0; /* not affected by preceding REST */ 17801592Srgrimes } 1781101537Syar if (unique) /* STOU overrides REST */ 1782101537Syar restart_point = 0; 1783101537Syar if (guest && noguestmod) { 1784101537Syar if (restart_point) { /* guest STOR w/REST */ 1785137852Syar reply(550, "Modifying existing file denied."); 1786101537Syar goto err; 1787101537Syar } else /* treat guest STOR as STOU */ 1788101537Syar unique = 1; 1789101537Syar } 17901592Srgrimes 17911592Srgrimes if (restart_point) 1792101537Syar mode = "r+"; /* so ASCII manual seek can work */ 1793101537Syar if (unique) { 1794101537Syar if ((fd = guniquefd(name, &name)) < 0) 1795101537Syar goto err; 1796101537Syar fout = fdopen(fd, mode); 1797101537Syar } else 1798101537Syar fout = fopen(name, mode); 17991592Srgrimes closefunc = fclose; 18001592Srgrimes if (fout == NULL) { 18011592Srgrimes perror_reply(553, name); 1802101537Syar goto err; 18031592Srgrimes } 18041592Srgrimes byte_count = -1; 18051592Srgrimes if (restart_point) { 18061592Srgrimes if (type == TYPE_A) { 18071592Srgrimes off_t i, n; 18081592Srgrimes int c; 18091592Srgrimes 18101592Srgrimes n = restart_point; 18111592Srgrimes i = 0; 18121592Srgrimes while (i++ < n) { 18131592Srgrimes if ((c=getc(fout)) == EOF) { 18141592Srgrimes perror_reply(550, name); 18151592Srgrimes goto done; 18161592Srgrimes } 18171592Srgrimes if (c == '\n') 18181592Srgrimes i++; 18191592Srgrimes } 18201592Srgrimes /* 18211592Srgrimes * We must do this seek to "current" position 18221592Srgrimes * because we are changing from reading to 18231592Srgrimes * writing. 18241592Srgrimes */ 1825132930Syar if (fseeko(fout, 0, SEEK_CUR) < 0) { 18261592Srgrimes perror_reply(550, name); 18271592Srgrimes goto done; 18281592Srgrimes } 18291592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 18301592Srgrimes perror_reply(550, name); 18311592Srgrimes goto done; 18321592Srgrimes } 18331592Srgrimes } 1834132930Syar din = dataconn(name, -1, "r"); 18351592Srgrimes if (din == NULL) 18361592Srgrimes goto done; 18371592Srgrimes if (receive_data(din, fout) == 0) { 18381592Srgrimes if (unique) 18391592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 18401592Srgrimes name); 18411592Srgrimes else 18421592Srgrimes reply(226, "Transfer complete."); 18431592Srgrimes } 18441592Srgrimes (void) fclose(din); 18451592Srgrimes data = -1; 18461592Srgrimes pdata = -1; 18471592Srgrimesdone: 1848102566Syar LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); 18491592Srgrimes (*closefunc)(fout); 1850101537Syar return; 1851101537Syarerr: 1852101537Syar LOGCMD(*mode == 'a' ? "append" : "put" , name); 1853101537Syar return; 18541592Srgrimes} 18551592Srgrimes 18561592Srgrimesstatic FILE * 185790148Simpgetdatasock(char *mode) 18581592Srgrimes{ 18591592Srgrimes int on = 1, s, t, tries; 18601592Srgrimes 18611592Srgrimes if (data >= 0) 18621592Srgrimes return (fdopen(data, mode)); 186356668Sshin 186456668Sshin s = socket(data_dest.su_family, SOCK_STREAM, 0); 18651592Srgrimes if (s < 0) 18661592Srgrimes goto bad; 1867100612Syar if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1868100609Syar syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 18691592Srgrimes /* anchor socket to avoid multi-homing problems */ 187056668Sshin data_source = ctrl_addr; 1871109742Syar data_source.su_port = htons(dataport); 1872132893Syar (void) seteuid(0); 18731592Srgrimes for (tries = 1; ; tries++) { 1874132891Syar /* 1875132891Syar * We should loop here since it's possible that 1876132891Syar * another ftpd instance has passed this point and is 1877132891Syar * trying to open a data connection in active mode now. 1878132891Syar * Until the other connection is opened, we'll be getting 1879132891Syar * EADDRINUSE because no SOCK_STREAM sockets in the system 1880132891Syar * can share both local and remote addresses, localIP:20 1881132891Syar * and *:* in this case. 1882132891Syar */ 18831592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 188456668Sshin data_source.su_len) >= 0) 18851592Srgrimes break; 18861592Srgrimes if (errno != EADDRINUSE || tries > 10) 18871592Srgrimes goto bad; 18881592Srgrimes sleep(tries); 18891592Srgrimes } 1890132893Syar (void) seteuid(pw->pw_uid); 18911592Srgrimes#ifdef IP_TOS 189256668Sshin if (data_source.su_family == AF_INET) 189356668Sshin { 18941592Srgrimes on = IPTOS_THROUGHPUT; 1895100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 1896100609Syar syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 189756668Sshin } 18981592Srgrimes#endif 18998240Swollman#ifdef TCP_NOPUSH 19008240Swollman /* 19018240Swollman * Turn off push flag to keep sender TCP from sending short packets 1902166598Syar * at the boundaries of each write(). 19038240Swollman */ 19048240Swollman on = 1; 1905100612Syar if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 1906100609Syar syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 19078240Swollman#endif 19081592Srgrimes return (fdopen(s, mode)); 19091592Srgrimesbad: 19101592Srgrimes /* Return the real value of errno (close may change it) */ 19111592Srgrimes t = errno; 1912132893Syar (void) seteuid(pw->pw_uid); 19131592Srgrimes (void) close(s); 19141592Srgrimes errno = t; 19151592Srgrimes return (NULL); 19161592Srgrimes} 19171592Srgrimes 19181592Srgrimesstatic FILE * 191990148Simpdataconn(char *name, off_t size, char *mode) 19201592Srgrimes{ 19211592Srgrimes char sizebuf[32]; 19221592Srgrimes FILE *file; 1923109611Scjc int retry = 0, tos, conerrno; 19241592Srgrimes 19251592Srgrimes file_size = size; 19261592Srgrimes byte_count = 0; 1927132930Syar if (size != -1) 1928132929Syar (void) snprintf(sizebuf, sizeof(sizebuf), 1929132929Syar " (%jd bytes)", (intmax_t)size); 19301592Srgrimes else 193131973Simp *sizebuf = '\0'; 19321592Srgrimes if (pdata >= 0) { 193356668Sshin union sockunion from; 1934141918Sstefanf socklen_t fromlen = ctrl_addr.su_len; 1935141918Sstefanf int flags, s; 193612532Sguido struct timeval timeout; 193712532Sguido fd_set set; 19381592Srgrimes 193912532Sguido FD_ZERO(&set); 194012532Sguido FD_SET(pdata, &set); 194112532Sguido 194212532Sguido timeout.tv_usec = 0; 194312532Sguido timeout.tv_sec = 120; 194412532Sguido 194586628Syar /* 194686628Syar * Granted a socket is in the blocking I/O mode, 194786628Syar * accept() will block after a successful select() 194886628Syar * if the selected connection dies in between. 194986628Syar * Therefore set the non-blocking I/O flag here. 195086628Syar */ 195186628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 195286628Syar fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 195386628Syar goto pdata_err; 1954132931Syar if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || 195586628Syar (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 195686628Syar goto pdata_err; 19571592Srgrimes (void) close(pdata); 19581592Srgrimes pdata = s; 195986628Syar /* 1960101809Syar * Unset the inherited non-blocking I/O flag 1961101809Syar * on the child socket so stdio can work on it. 196286628Syar */ 196386628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 196486628Syar fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 196586628Syar goto pdata_err; 19661592Srgrimes#ifdef IP_TOS 196756668Sshin if (from.su_family == AF_INET) 196856668Sshin { 196917435Spst tos = IPTOS_THROUGHPUT; 1970100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 1971100609Syar syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 197256668Sshin } 19731592Srgrimes#endif 19741592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19751592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19761592Srgrimes return (fdopen(pdata, mode)); 197786628Syarpdata_err: 197886628Syar reply(425, "Can't open data connection."); 197986628Syar (void) close(pdata); 198086628Syar pdata = -1; 198186628Syar return (NULL); 19821592Srgrimes } 19831592Srgrimes if (data >= 0) { 19841592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 19851592Srgrimes name, sizebuf); 19861592Srgrimes usedefault = 1; 19871592Srgrimes return (fdopen(data, mode)); 19881592Srgrimes } 19891592Srgrimes if (usedefault) 19901592Srgrimes data_dest = his_addr; 19911592Srgrimes usedefault = 1; 1992109611Scjc do { 1993109611Scjc file = getdatasock(mode); 1994109611Scjc if (file == NULL) { 1995137983Syar char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 1996137983Syar 1997137983Syar if (getnameinfo((struct sockaddr *)&data_source, 1998137983Syar data_source.su_len, 1999137983Syar hostbuf, sizeof(hostbuf) - 1, 2000137983Syar portbuf, sizeof(portbuf) - 1, 2001137983Syar NI_NUMERICHOST|NI_NUMERICSERV)) 2002137983Syar *hostbuf = *portbuf = 0; 2003137983Syar hostbuf[sizeof(hostbuf) - 1] = 0; 2004137983Syar portbuf[sizeof(portbuf) - 1] = 0; 2005109611Scjc reply(425, "Can't create data socket (%s,%s): %s.", 2006109611Scjc hostbuf, portbuf, strerror(errno)); 2007109611Scjc return (NULL); 2008109611Scjc } 2009109611Scjc data = fileno(file); 2010109611Scjc conerrno = 0; 2011109611Scjc if (connect(data, (struct sockaddr *)&data_dest, 2012109611Scjc data_dest.su_len) == 0) 2013109611Scjc break; 2014109611Scjc conerrno = errno; 2015109611Scjc (void) fclose(file); 2016109611Scjc data = -1; 2017109611Scjc if (conerrno == EADDRINUSE) { 2018137659Syar sleep(swaitint); 20191592Srgrimes retry += swaitint; 2020109611Scjc } else { 2021109611Scjc break; 20221592Srgrimes } 2023109611Scjc } while (retry <= swaitmax); 2024109611Scjc if (conerrno != 0) { 2025137850Syar reply(425, "Can't build data connection: %s.", 2026137850Syar strerror(conerrno)); 20271592Srgrimes return (NULL); 20281592Srgrimes } 20291592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 20301592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 20311592Srgrimes return (file); 20321592Srgrimes} 20331592Srgrimes 20341592Srgrimes/* 2035140472Syar * A helper macro to avoid code duplication 2036140472Syar * in send_data() and receive_data(). 2037140472Syar * 2038140472Syar * XXX We have to block SIGURG during putc() because BSD stdio 2039140472Syar * is unable to restart interrupted write operations and hence 2040140472Syar * the entire buffer contents will be lost as soon as a write() 2041140472Syar * call indicates EINTR to stdio. 2042140472Syar */ 2043140472Syar#define FTPD_PUTC(ch, file, label) \ 2044140472Syar do { \ 2045140472Syar int ret; \ 2046140472Syar \ 2047140472Syar do { \ 2048140472Syar START_UNSAFE; \ 2049140472Syar ret = putc((ch), (file)); \ 2050140472Syar END_UNSAFE; \ 2051140472Syar CHECKOOB(return (-1)) \ 2052140472Syar else if (ferror(file)) \ 2053140472Syar goto label; \ 2054140472Syar clearerr(file); \ 2055140472Syar } while (ret == EOF); \ 2056140472Syar } while (0) 2057140472Syar 2058140472Syar/* 20591592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate 20608240Swollman * encapsulation of the data subject to Mode, Structure, and Type. 20611592Srgrimes * 20621592Srgrimes * NB: Form isn't handled. 20631592Srgrimes */ 206489935Syarstatic int 2065137660Syarsend_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg) 20661592Srgrimes{ 2067122751Syar int c, cp, filefd, netfd; 206870205Sdan char *buf; 20691592Srgrimes 2070140472Syar STARTXFER; 2071140472Syar 20721592Srgrimes switch (type) { 20731592Srgrimes 20741592Srgrimes case TYPE_A: 2075140472Syar cp = EOF; 2076140472Syar for (;;) { 2077140472Syar c = getc(instr); 2078140472Syar CHECKOOB(return (-1)) 2079140472Syar else if (c == EOF && ferror(instr)) 2080140472Syar goto file_err; 2081140472Syar if (c == EOF) { 2082140472Syar if (ferror(instr)) { /* resume after OOB */ 2083140472Syar clearerr(instr); 2084140472Syar continue; 2085140472Syar } 2086140472Syar if (feof(instr)) /* EOF */ 2087140472Syar break; 2088140472Syar syslog(LOG_ERR, "Internal: impossible condition" 2089140472Syar " on file after getc()"); 2090140472Syar goto file_err; 2091140472Syar } 2092122751Syar if (c == '\n' && cp != '\r') { 2093140472Syar FTPD_PUTC('\r', outstr, data_err); 2094140472Syar byte_count++; 20951592Srgrimes } 2096140472Syar FTPD_PUTC(c, outstr, data_err); 2097140472Syar byte_count++; 2098122751Syar cp = c; 20991592Srgrimes } 2100140472Syar#ifdef notyet /* BSD stdio isn't ready for that */ 2101140472Syar while (fflush(outstr) == EOF) { 2102140472Syar CHECKOOB(return (-1)) 2103140472Syar else 2104140472Syar goto data_err; 2105140472Syar clearerr(outstr); 2106140472Syar } 2107140472Syar ENDXFER; 2108140472Syar#else 2109140472Syar ENDXFER; 2110140472Syar if (fflush(outstr) == EOF) 21111592Srgrimes goto data_err; 2112140472Syar#endif 21131592Srgrimes reply(226, "Transfer complete."); 211489935Syar return (0); 21151592Srgrimes 21161592Srgrimes case TYPE_I: 21171592Srgrimes case TYPE_L: 21188240Swollman /* 21198240Swollman * isreg is only set if we are not doing restart and we 21208240Swollman * are sending a regular file 21218240Swollman */ 21228240Swollman netfd = fileno(outstr); 21238870Srgrimes filefd = fileno(instr); 21248240Swollman 212570205Sdan if (isreg) { 2126136554Syar char *msg = "Transfer complete."; 2127140472Syar off_t cnt, offset; 212870205Sdan int err; 212970205Sdan 2130136555Syar cnt = offset = 0; 213170205Sdan 2132136555Syar while (filesize > 0) { 213390604Smaxim err = sendfile(filefd, netfd, offset, 0, 2134137811Syar NULL, &cnt, 0); 213599212Smaxim /* 213699212Smaxim * Calculate byte_count before OOB processing. 213799212Smaxim * It can be used in myoob() later. 213899212Smaxim */ 213999212Smaxim byte_count += cnt; 214070205Sdan offset += cnt; 214190604Smaxim filesize -= cnt; 2142140472Syar CHECKOOB(return (-1)) 2143140472Syar else if (err == -1) { 2144140472Syar if (errno != EINTR && 2145140472Syar cnt == 0 && offset == 0) 214670205Sdan goto oldway; 214770205Sdan goto data_err; 214870205Sdan } 2149140472Syar if (err == -1) /* resume after OOB */ 2150140472Syar continue; 2151136554Syar /* 2152136554Syar * We hit the EOF prematurely. 2153136554Syar * Perhaps the file was externally truncated. 2154136554Syar */ 2155136554Syar if (cnt == 0) { 2156136554Syar msg = "Transfer finished due to " 2157136554Syar "premature end of file."; 2158136554Syar break; 2159136554Syar } 216070205Sdan } 2161140472Syar ENDXFER; 2162216945Semaste reply(226, "%s", msg); 216389935Syar return (0); 21648240Swollman } 21658240Swollman 21668240Swollmanoldway: 2167137659Syar if ((buf = malloc(blksize)) == NULL) { 2168140472Syar ENDXFER; 2169137852Syar reply(451, "Ran out of memory."); 217089935Syar return (-1); 21711592Srgrimes } 21728870Srgrimes 2173140472Syar for (;;) { 2174140472Syar int cnt, len; 2175140472Syar char *bp; 2176140472Syar 2177140472Syar cnt = read(filefd, buf, blksize); 2178140472Syar CHECKOOB(free(buf); return (-1)) 2179140472Syar else if (cnt < 0) { 2180140472Syar free(buf); 21811592Srgrimes goto file_err; 2182140472Syar } 2183140472Syar if (cnt < 0) /* resume after OOB */ 2184140472Syar continue; 2185140472Syar if (cnt == 0) /* EOF */ 2186140472Syar break; 2187140472Syar for (len = cnt, bp = buf; len > 0;) { 2188140472Syar cnt = write(netfd, bp, len); 2189140472Syar CHECKOOB(free(buf); return (-1)) 2190140472Syar else if (cnt < 0) { 2191140472Syar free(buf); 2192140472Syar goto data_err; 2193140472Syar } 2194140472Syar if (cnt <= 0) 2195140472Syar continue; 2196140472Syar len -= cnt; 2197140472Syar bp += cnt; 2198140472Syar byte_count += cnt; 2199140472Syar } 22001592Srgrimes } 2201140472Syar ENDXFER; 2202140472Syar free(buf); 22031592Srgrimes reply(226, "Transfer complete."); 220489935Syar return (0); 22051592Srgrimes default: 2206140472Syar ENDXFER; 2207137852Syar reply(550, "Unimplemented TYPE %d in send_data.", type); 220889935Syar return (-1); 22091592Srgrimes } 22101592Srgrimes 22111592Srgrimesdata_err: 2212140472Syar ENDXFER; 22131592Srgrimes perror_reply(426, "Data connection"); 221489935Syar return (-1); 22151592Srgrimes 22161592Srgrimesfile_err: 2217140472Syar ENDXFER; 22181592Srgrimes perror_reply(551, "Error on input file"); 221989935Syar return (-1); 22201592Srgrimes} 22211592Srgrimes 22221592Srgrimes/* 22231592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 22241592Srgrimes * the data subject to Mode, Structure, and Type. 22251592Srgrimes * 22261592Srgrimes * N.B.: Form isn't handled. 22271592Srgrimes */ 22281592Srgrimesstatic int 222990148Simpreceive_data(FILE *instr, FILE *outstr) 22301592Srgrimes{ 2231140472Syar int c, cp; 2232140472Syar int bare_lfs = 0; 22331592Srgrimes 2234140472Syar STARTXFER; 223517433Spst 22361592Srgrimes switch (type) { 22371592Srgrimes 22381592Srgrimes case TYPE_I: 22391592Srgrimes case TYPE_L: 2240140472Syar for (;;) { 2241140472Syar int cnt, len; 2242140472Syar char *bp; 2243140472Syar char buf[BUFSIZ]; 2244140472Syar 2245140472Syar cnt = read(fileno(instr), buf, sizeof(buf)); 2246140472Syar CHECKOOB(return (-1)) 2247140472Syar else if (cnt < 0) 2248140472Syar goto data_err; 2249140472Syar if (cnt < 0) /* resume after OOB */ 2250140472Syar continue; 2251140472Syar if (cnt == 0) /* EOF */ 2252140472Syar break; 2253140472Syar for (len = cnt, bp = buf; len > 0;) { 2254140472Syar cnt = write(fileno(outstr), bp, len); 2255140472Syar CHECKOOB(return (-1)) 2256140472Syar else if (cnt < 0) 2257140472Syar goto file_err; 2258140472Syar if (cnt <= 0) 2259140472Syar continue; 2260140472Syar len -= cnt; 2261140472Syar bp += cnt; 2262140472Syar byte_count += cnt; 2263140472Syar } 22641592Srgrimes } 2265140472Syar ENDXFER; 22661592Srgrimes return (0); 22671592Srgrimes 22681592Srgrimes case TYPE_E: 2269140472Syar ENDXFER; 22701592Srgrimes reply(553, "TYPE E not implemented."); 22711592Srgrimes return (-1); 22721592Srgrimes 22731592Srgrimes case TYPE_A: 2274140472Syar cp = EOF; 2275140472Syar for (;;) { 2276140472Syar c = getc(instr); 2277140472Syar CHECKOOB(return (-1)) 2278140472Syar else if (c == EOF && ferror(instr)) 2279140472Syar goto data_err; 2280140472Syar if (c == EOF && ferror(instr)) { /* resume after OOB */ 2281140472Syar clearerr(instr); 2282140472Syar continue; 2283140472Syar } 2284140472Syar 2285140472Syar if (cp == '\r') { 2286140472Syar if (c != '\n') 2287140472Syar FTPD_PUTC('\r', outstr, file_err); 2288140472Syar } else 2289140472Syar if (c == '\n') 2290140472Syar bare_lfs++; 2291140472Syar if (c == '\r') { 2292140472Syar byte_count++; 2293140472Syar cp = c; 2294140472Syar continue; 2295140472Syar } 2296140472Syar 2297140472Syar /* Check for EOF here in order not to lose last \r. */ 2298140472Syar if (c == EOF) { 2299140472Syar if (feof(instr)) /* EOF */ 2300140472Syar break; 2301140472Syar syslog(LOG_ERR, "Internal: impossible condition" 2302140472Syar " on data stream after getc()"); 2303140472Syar goto data_err; 2304140472Syar } 2305140472Syar 23061592Srgrimes byte_count++; 2307140472Syar FTPD_PUTC(c, outstr, file_err); 2308140472Syar cp = c; 23091592Srgrimes } 2310140472Syar#ifdef notyet /* BSD stdio isn't ready for that */ 2311140472Syar while (fflush(outstr) == EOF) { 2312140472Syar CHECKOOB(return (-1)) 2313140472Syar else 2314140472Syar goto file_err; 2315140472Syar clearerr(outstr); 2316140472Syar } 2317140472Syar ENDXFER; 2318140472Syar#else 2319140472Syar ENDXFER; 2320140472Syar if (fflush(outstr) == EOF) 23211592Srgrimes goto file_err; 2322140472Syar#endif 23231592Srgrimes if (bare_lfs) { 23241592Srgrimes lreply(226, 2325137852Syar "WARNING! %d bare linefeeds received in ASCII mode.", 23261592Srgrimes bare_lfs); 23271592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 23281592Srgrimes } 23291592Srgrimes return (0); 23301592Srgrimes default: 2331140472Syar ENDXFER; 2332137852Syar reply(550, "Unimplemented TYPE %d in receive_data.", type); 23331592Srgrimes return (-1); 23341592Srgrimes } 23351592Srgrimes 23361592Srgrimesdata_err: 2337140472Syar ENDXFER; 2338137852Syar perror_reply(426, "Data connection"); 23391592Srgrimes return (-1); 23401592Srgrimes 23411592Srgrimesfile_err: 2342140472Syar ENDXFER; 2343137852Syar perror_reply(452, "Error writing to file"); 23441592Srgrimes return (-1); 23451592Srgrimes} 23461592Srgrimes 23471592Srgrimesvoid 234890148Simpstatfilecmd(char *filename) 23491592Srgrimes{ 23501592Srgrimes FILE *fin; 2351109382Syar int atstart; 2352137728Syar int c, code; 23531592Srgrimes char line[LINE_MAX]; 2354137728Syar struct stat st; 23551592Srgrimes 2356137728Syar code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; 235725165Sdavidn (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 23581592Srgrimes fin = ftpd_popen(line, "r"); 2359216943Semaste if (fin == NULL) { 2360216943Semaste perror_reply(551, filename); 2361216943Semaste return; 2362216943Semaste } 2363137729Syar lreply(code, "Status of %s:", filename); 2364109382Syar atstart = 1; 23651592Srgrimes while ((c = getc(fin)) != EOF) { 23661592Srgrimes if (c == '\n') { 23671592Srgrimes if (ferror(stdout)){ 2368137852Syar perror_reply(421, "Control connection"); 23691592Srgrimes (void) ftpd_pclose(fin); 23701592Srgrimes dologout(1); 23711592Srgrimes /* NOTREACHED */ 23721592Srgrimes } 23731592Srgrimes if (ferror(fin)) { 23741592Srgrimes perror_reply(551, filename); 23751592Srgrimes (void) ftpd_pclose(fin); 23761592Srgrimes return; 23771592Srgrimes } 23781592Srgrimes (void) putc('\r', stdout); 23791592Srgrimes } 2380109382Syar /* 2381109382Syar * RFC 959 says neutral text should be prepended before 2382109382Syar * a leading 3-digit number followed by whitespace, but 2383109382Syar * many ftp clients can be confused by any leading digits, 2384109382Syar * as a matter of fact. 2385109382Syar */ 2386109382Syar if (atstart && isdigit(c)) 2387109382Syar (void) putc(' ', stdout); 23881592Srgrimes (void) putc(c, stdout); 2389109382Syar atstart = (c == '\n'); 23901592Srgrimes } 23911592Srgrimes (void) ftpd_pclose(fin); 2392137852Syar reply(code, "End of status."); 23931592Srgrimes} 23941592Srgrimes 23951592Srgrimesvoid 239690148Simpstatcmd(void) 23971592Srgrimes{ 239856668Sshin union sockunion *su; 23991592Srgrimes u_char *a, *p; 240099255Sume char hname[NI_MAXHOST]; 240156668Sshin int ispassive; 24021592Srgrimes 2403110037Syar if (hostinfo) { 2404110037Syar lreply(211, "%s FTP server status:", hostname); 2405110037Syar printf(" %s\r\n", version); 2406110037Syar } else 2407110037Syar lreply(211, "FTP server status:"); 24081592Srgrimes printf(" Connected to %s", remotehost); 240956668Sshin if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 241099255Sume hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 2411137983Syar hname[sizeof(hname) - 1] = 0; 241256668Sshin if (strcmp(hname, remotehost) != 0) 241356668Sshin printf(" (%s)", hname); 241456668Sshin } 24151592Srgrimes printf("\r\n"); 24161592Srgrimes if (logged_in) { 24171592Srgrimes if (guest) 24181592Srgrimes printf(" Logged in anonymously\r\n"); 24191592Srgrimes else 24201592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 24211592Srgrimes } else if (askpasswd) 24221592Srgrimes printf(" Waiting for password\r\n"); 24231592Srgrimes else 24241592Srgrimes printf(" Waiting for user name\r\n"); 24251592Srgrimes printf(" TYPE: %s", typenames[type]); 24261592Srgrimes if (type == TYPE_A || type == TYPE_E) 24271592Srgrimes printf(", FORM: %s", formnames[form]); 24281592Srgrimes if (type == TYPE_L) 2429103949Smike#if CHAR_BIT == 8 2430103949Smike printf(" %d", CHAR_BIT); 24311592Srgrimes#else 24321592Srgrimes printf(" %d", bytesize); /* need definition! */ 24331592Srgrimes#endif 24341592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 24351592Srgrimes strunames[stru], modenames[mode]); 24361592Srgrimes if (data != -1) 24371592Srgrimes printf(" Data connection open\r\n"); 24381592Srgrimes else if (pdata != -1) { 243956668Sshin ispassive = 1; 244056668Sshin su = &pasv_addr; 24411592Srgrimes goto printaddr; 24421592Srgrimes } else if (usedefault == 0) { 244356668Sshin ispassive = 0; 244456668Sshin su = &data_dest; 24451592Srgrimesprintaddr: 24461592Srgrimes#define UC(b) (((int) b) & 0xff) 244756668Sshin if (epsvall) { 244856668Sshin printf(" EPSV only mode (EPSV ALL)\r\n"); 244956668Sshin goto epsvonly; 245056668Sshin } 245156668Sshin 245256668Sshin /* PORT/PASV */ 245356668Sshin if (su->su_family == AF_INET) { 245456668Sshin a = (u_char *) &su->su_sin.sin_addr; 245556668Sshin p = (u_char *) &su->su_sin.sin_port; 245656668Sshin printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 245756668Sshin ispassive ? "PASV" : "PORT", 245856668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 245956668Sshin UC(p[0]), UC(p[1])); 246056668Sshin } 246156668Sshin 246256668Sshin /* LPRT/LPSV */ 246356668Sshin { 246456668Sshin int alen, af, i; 246556668Sshin 246656668Sshin switch (su->su_family) { 246756668Sshin case AF_INET: 246856668Sshin a = (u_char *) &su->su_sin.sin_addr; 246956668Sshin p = (u_char *) &su->su_sin.sin_port; 247056668Sshin alen = sizeof(su->su_sin.sin_addr); 247156668Sshin af = 4; 247256668Sshin break; 247356668Sshin case AF_INET6: 247456668Sshin a = (u_char *) &su->su_sin6.sin6_addr; 247556668Sshin p = (u_char *) &su->su_sin6.sin6_port; 247656668Sshin alen = sizeof(su->su_sin6.sin6_addr); 247756668Sshin af = 6; 247856668Sshin break; 247956668Sshin default: 248056668Sshin af = 0; 248156668Sshin break; 248256668Sshin } 248356668Sshin if (af) { 248456668Sshin printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 248556668Sshin af, alen); 248656668Sshin for (i = 0; i < alen; i++) 248756668Sshin printf("%d,", UC(a[i])); 248856668Sshin printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 248956668Sshin } 249056668Sshin } 249156668Sshin 249256668Sshinepsvonly:; 249356668Sshin /* EPRT/EPSV */ 249456668Sshin { 249556668Sshin int af; 249656668Sshin 249756668Sshin switch (su->su_family) { 249856668Sshin case AF_INET: 249956668Sshin af = 1; 250056668Sshin break; 250156668Sshin case AF_INET6: 250256668Sshin af = 2; 250356668Sshin break; 250456668Sshin default: 250556668Sshin af = 0; 250656668Sshin break; 250756668Sshin } 250856668Sshin if (af) { 250999255Sume union sockunion tmp; 251099255Sume 251199255Sume tmp = *su; 251299255Sume if (tmp.su_family == AF_INET6) 251399255Sume tmp.su_sin6.sin6_scope_id = 0; 251499255Sume if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 251556668Sshin hname, sizeof(hname) - 1, NULL, 0, 251656668Sshin NI_NUMERICHOST)) { 2517137983Syar hname[sizeof(hname) - 1] = 0; 251856668Sshin printf(" %s |%d|%s|%d|\r\n", 251956668Sshin ispassive ? "EPSV" : "EPRT", 252099255Sume af, hname, htons(tmp.su_port)); 252156668Sshin } 252256668Sshin } 252356668Sshin } 25241592Srgrimes#undef UC 25251592Srgrimes } else 25261592Srgrimes printf(" No data connection\r\n"); 2527137852Syar reply(211, "End of status."); 25281592Srgrimes} 25291592Srgrimes 25301592Srgrimesvoid 253190148Simpfatalerror(char *s) 25321592Srgrimes{ 25331592Srgrimes 2534137849Syar reply(451, "Error in server: %s", s); 25351592Srgrimes reply(221, "Closing connection due to server error."); 25361592Srgrimes dologout(0); 25371592Srgrimes /* NOTREACHED */ 25381592Srgrimes} 25391592Srgrimes 25401592Srgrimesvoid 25411592Srgrimesreply(int n, const char *fmt, ...) 25421592Srgrimes{ 25431592Srgrimes va_list ap; 254490148Simp 2545129170Stjr (void)printf("%d ", n); 25461592Srgrimes va_start(ap, fmt); 25471592Srgrimes (void)vprintf(fmt, ap); 2548129170Stjr va_end(ap); 25491592Srgrimes (void)printf("\r\n"); 25501592Srgrimes (void)fflush(stdout); 255176096Smarkm if (ftpdebug) { 25521592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 2553129170Stjr va_start(ap, fmt); 25541592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2555129170Stjr va_end(ap); 25561592Srgrimes } 25571592Srgrimes} 25581592Srgrimes 25591592Srgrimesvoid 25601592Srgrimeslreply(int n, const char *fmt, ...) 25611592Srgrimes{ 25621592Srgrimes va_list ap; 256390148Simp 2564129170Stjr (void)printf("%d- ", n); 25651592Srgrimes va_start(ap, fmt); 25661592Srgrimes (void)vprintf(fmt, ap); 2567129170Stjr va_end(ap); 25681592Srgrimes (void)printf("\r\n"); 25691592Srgrimes (void)fflush(stdout); 257076096Smarkm if (ftpdebug) { 25711592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 2572129170Stjr va_start(ap, fmt); 25731592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2574129170Stjr va_end(ap); 25751592Srgrimes } 25761592Srgrimes} 25771592Srgrimes 25781592Srgrimesstatic void 257990148Simpack(char *s) 25801592Srgrimes{ 25811592Srgrimes 25821592Srgrimes reply(250, "%s command successful.", s); 25831592Srgrimes} 25841592Srgrimes 25851592Srgrimesvoid 258690148Simpnack(char *s) 25871592Srgrimes{ 25881592Srgrimes 25891592Srgrimes reply(502, "%s command not implemented.", s); 25901592Srgrimes} 25911592Srgrimes 25921592Srgrimes/* ARGSUSED */ 25931592Srgrimesvoid 259490148Simpyyerror(char *s) 25951592Srgrimes{ 25961592Srgrimes char *cp; 25971592Srgrimes 259817478Smarkm if ((cp = strchr(cbuf,'\n'))) 25991592Srgrimes *cp = '\0'; 2600137852Syar reply(500, "%s: command not understood.", cbuf); 26011592Srgrimes} 26021592Srgrimes 26031592Srgrimesvoid 260490148Simpdelete(char *name) 26051592Srgrimes{ 26061592Srgrimes struct stat st; 26071592Srgrimes 26081592Srgrimes LOGCMD("delete", name); 2609100439Syar if (lstat(name, &st) < 0) { 26101592Srgrimes perror_reply(550, name); 26111592Srgrimes return; 26121592Srgrimes } 2613137847Syar if (S_ISDIR(st.st_mode)) { 26141592Srgrimes if (rmdir(name) < 0) { 26151592Srgrimes perror_reply(550, name); 26161592Srgrimes return; 26171592Srgrimes } 26181592Srgrimes goto done; 26191592Srgrimes } 2620125568Syar if (guest && noguestmod) { 2621137852Syar reply(550, "Operation not permitted."); 2622125568Syar return; 2623125568Syar } 2624125568Syar if (unlink(name) < 0) { 26251592Srgrimes perror_reply(550, name); 26261592Srgrimes return; 26271592Srgrimes } 26281592Srgrimesdone: 26291592Srgrimes ack("DELE"); 26301592Srgrimes} 26311592Srgrimes 26321592Srgrimesvoid 263390148Simpcwd(char *path) 26341592Srgrimes{ 26351592Srgrimes 26361592Srgrimes if (chdir(path) < 0) 26371592Srgrimes perror_reply(550, path); 26381592Srgrimes else 26391592Srgrimes ack("CWD"); 26401592Srgrimes} 26411592Srgrimes 26421592Srgrimesvoid 264390148Simpmakedir(char *name) 26441592Srgrimes{ 2645100878Syar char *s; 26461592Srgrimes 26471592Srgrimes LOGCMD("mkdir", name); 264899195Smdodd if (guest && noguestmkd) 2649137853Syar reply(550, "Operation not permitted."); 265099195Smdodd else if (mkdir(name, 0777) < 0) 26511592Srgrimes perror_reply(550, name); 2652100878Syar else { 2653100878Syar if ((s = doublequote(name)) == NULL) 2654100878Syar fatalerror("Ran out of memory."); 2655100878Syar reply(257, "\"%s\" directory created.", s); 2656100878Syar free(s); 2657100878Syar } 26581592Srgrimes} 26591592Srgrimes 26601592Srgrimesvoid 266190148Simpremovedir(char *name) 26621592Srgrimes{ 26631592Srgrimes 26641592Srgrimes LOGCMD("rmdir", name); 26651592Srgrimes if (rmdir(name) < 0) 26661592Srgrimes perror_reply(550, name); 26671592Srgrimes else 26681592Srgrimes ack("RMD"); 26691592Srgrimes} 26701592Srgrimes 26711592Srgrimesvoid 267290148Simppwd(void) 26731592Srgrimes{ 2674100486Syar char *s, path[MAXPATHLEN + 1]; 26751592Srgrimes 2676137830Syar if (getcwd(path, sizeof(path)) == NULL) 2677137839Syar perror_reply(550, "Get current directory"); 2678100486Syar else { 2679100486Syar if ((s = doublequote(path)) == NULL) 2680100486Syar fatalerror("Ran out of memory."); 2681100486Syar reply(257, "\"%s\" is current directory.", s); 2682100486Syar free(s); 2683100486Syar } 26841592Srgrimes} 26851592Srgrimes 26861592Srgrimeschar * 268790148Simprenamefrom(char *name) 26881592Srgrimes{ 26891592Srgrimes struct stat st; 26901592Srgrimes 2691125569Syar if (guest && noguestmod) { 2692137852Syar reply(550, "Operation not permitted."); 2693125569Syar return (NULL); 2694125569Syar } 2695100439Syar if (lstat(name, &st) < 0) { 26961592Srgrimes perror_reply(550, name); 2697125570Syar return (NULL); 26981592Srgrimes } 2699137852Syar reply(350, "File exists, ready for destination name."); 27001592Srgrimes return (name); 27011592Srgrimes} 27021592Srgrimes 27031592Srgrimesvoid 270490148Simprenamecmd(char *from, char *to) 27051592Srgrimes{ 270617433Spst struct stat st; 27071592Srgrimes 27081592Srgrimes LOGCMD2("rename", from, to); 270917433Spst 271017433Spst if (guest && (stat(to, &st) == 0)) { 2711137852Syar reply(550, "%s: permission denied.", to); 271217433Spst return; 271317433Spst } 271417433Spst 27151592Srgrimes if (rename(from, to) < 0) 27161592Srgrimes perror_reply(550, "rename"); 27171592Srgrimes else 27181592Srgrimes ack("RNTO"); 27191592Srgrimes} 27201592Srgrimes 27211592Srgrimesstatic void 272290148Simpdolog(struct sockaddr *who) 27231592Srgrimes{ 2724137984Syar char who_name[NI_MAXHOST]; 27251592Srgrimes 272656668Sshin realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 2727137983Syar remotehost[sizeof(remotehost) - 1] = 0; 2728137984Syar if (getnameinfo(who, who->sa_len, 2729137984Syar who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) 2730137984Syar *who_name = 0; 2731137984Syar who_name[sizeof(who_name) - 1] = 0; 273256668Sshin 27331592Srgrimes#ifdef SETPROCTITLE 273425283Sdavidn#ifdef VIRTUAL_HOSTING 273525283Sdavidn if (thishost != firsthost) 273625283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 273725283Sdavidn remotehost, hostname); 273825283Sdavidn else 273925283Sdavidn#endif 274025283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected", 274125283Sdavidn remotehost); 274213139Speter setproctitle("%s", proctitle); 27431592Srgrimes#endif /* SETPROCTITLE */ 27441592Srgrimes 274525283Sdavidn if (logging) { 274625283Sdavidn#ifdef VIRTUAL_HOSTING 274725283Sdavidn if (thishost != firsthost) 2748137984Syar syslog(LOG_INFO, "connection from %s (%s) to %s", 2749137984Syar remotehost, who_name, hostname); 275025283Sdavidn else 275125283Sdavidn#endif 2752137984Syar syslog(LOG_INFO, "connection from %s (%s)", 2753137984Syar remotehost, who_name); 275425283Sdavidn } 27551592Srgrimes} 27561592Srgrimes 27571592Srgrimes/* 27581592Srgrimes * Record logout in wtmp file 27591592Srgrimes * and exit with supplied status. 27601592Srgrimes */ 27611592Srgrimesvoid 276290148Simpdologout(int status) 27631592Srgrimes{ 27641592Srgrimes 2765202604Sed if (logged_in && dowtmp) { 2766132893Syar (void) seteuid(0); 2767325472Seugen#ifdef LOGIN_CAP 2768325472Seugen setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | 2769325472Seugen LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | 2770325472Seugen LOGIN_SETENV)); 2771325472Seugen#endif 2772202604Sed ftpd_logwtmp(wtmpid, NULL, NULL); 27731592Srgrimes } 27741592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 27751592Srgrimes _exit(status); 27761592Srgrimes} 27771592Srgrimes 27781592Srgrimesstatic void 277990148Simpsigurg(int signo) 27801592Srgrimes{ 278189935Syar 278289935Syar recvurg = 1; 278389935Syar} 278489935Syar 278589935Syarstatic void 2786140472Syarmaskurg(int flag) 2787140472Syar{ 2788140472Syar int oerrno; 2789140472Syar sigset_t sset; 2790140472Syar 2791140472Syar if (!transflag) { 2792140472Syar syslog(LOG_ERR, "Internal: maskurg() while no transfer"); 2793140472Syar return; 2794140472Syar } 2795140472Syar oerrno = errno; 2796140472Syar sigemptyset(&sset); 2797140472Syar sigaddset(&sset, SIGURG); 2798140472Syar sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); 2799140472Syar errno = oerrno; 2800140472Syar} 2801140472Syar 2802140472Syarstatic void 2803140472Syarflagxfer(int flag) 2804140472Syar{ 2805140472Syar 2806140472Syar if (flag) { 2807141967Syar if (transflag) 2808141967Syar syslog(LOG_ERR, "Internal: flagxfer(1): " 2809141967Syar "transfer already under way"); 2810141966Syar transflag = 1; 2811141966Syar maskurg(0); 2812140472Syar recvurg = 0; 2813141966Syar } else { 2814141967Syar if (!transflag) 2815141967Syar syslog(LOG_ERR, "Internal: flagxfer(0): " 2816141967Syar "no active transfer"); 2817141966Syar maskurg(1); 2818140472Syar transflag = 0; 2819141966Syar } 2820140472Syar} 2821140472Syar 2822140472Syar/* 2823140472Syar * Returns 0 if OK to resume or -1 if abort requested. 2824140472Syar */ 2825140472Syarstatic int 282690148Simpmyoob(void) 282789935Syar{ 28281592Srgrimes char *cp; 2829186405Scperciva int ret; 28301592Srgrimes 2831140472Syar if (!transflag) { 2832140472Syar syslog(LOG_ERR, "Internal: myoob() while no transfer"); 2833140472Syar return (0); 2834140472Syar } 28351592Srgrimes cp = tmpline; 2836186405Scperciva ret = getline(cp, 7, stdin); 2837186405Scperciva if (ret == -1) { 28381592Srgrimes reply(221, "You could at least say goodbye."); 28391592Srgrimes dologout(0); 2840186405Scperciva } else if (ret == -2) { 2841186405Scperciva /* Ignore truncated command. */ 2842186405Scperciva return (0); 28431592Srgrimes } 28441592Srgrimes upper(cp); 28451592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 28461592Srgrimes tmpline[0] = '\0'; 28471592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 2848137852Syar reply(226, "Abort successful."); 2849140472Syar return (-1); 28501592Srgrimes } 28511592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 285251192Smharo tmpline[0] = '\0'; 2853132930Syar if (file_size != -1) 2854137852Syar reply(213, "Status: %jd of %jd bytes transferred.", 2855132929Syar (intmax_t)byte_count, (intmax_t)file_size); 28561592Srgrimes else 2857137852Syar reply(213, "Status: %jd bytes transferred.", 2858132929Syar (intmax_t)byte_count); 28591592Srgrimes } 2860140472Syar return (0); 28611592Srgrimes} 28621592Srgrimes 28631592Srgrimes/* 28641592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 28651592Srgrimes * the PASV command in RFC959. However, it has been blessed as 28661592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 28671592Srgrimes * with Rick Adams on 25 Jan 89. 28681592Srgrimes */ 28691592Srgrimesvoid 287090148Simppassive(void) 28711592Srgrimes{ 2872141918Sstefanf socklen_t len; 2873141918Sstefanf int on; 28741592Srgrimes char *p, *a; 28751592Srgrimes 287617433Spst if (pdata >= 0) /* close old port if one set */ 287717433Spst close(pdata); 287817433Spst 287956668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 28801592Srgrimes if (pdata < 0) { 28811592Srgrimes perror_reply(425, "Can't open passive connection"); 28821592Srgrimes return; 28831592Srgrimes } 2884100615Syar on = 1; 2885100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2886100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 28879933Spst 2888132893Syar (void) seteuid(0); 288917433Spst 289019903Spst#ifdef IP_PORTRANGE 289156668Sshin if (ctrl_addr.su_family == AF_INET) { 2892100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2893100615Syar : IP_PORTRANGE_DEFAULT; 289419903Spst 289519903Spst if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2896100612Syar &on, sizeof(on)) < 0) 289719903Spst goto pasv_error; 28981592Srgrimes } 289919903Spst#endif 290060929Snsayer#ifdef IPV6_PORTRANGE 290160929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2902100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2903100615Syar : IPV6_PORTRANGE_DEFAULT; 29049933Spst 290560929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2906100612Syar &on, sizeof(on)) < 0) 290760929Snsayer goto pasv_error; 290860929Snsayer } 290960929Snsayer#endif 291060929Snsayer 291116033Speter pasv_addr = ctrl_addr; 291256668Sshin pasv_addr.su_port = 0; 291356668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 291416033Speter goto pasv_error; 291517433Spst 2916132893Syar (void) seteuid(pw->pw_uid); 291716033Speter 29181592Srgrimes len = sizeof(pasv_addr); 29191592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 29201592Srgrimes goto pasv_error; 29211592Srgrimes if (listen(pdata, 1) < 0) 29221592Srgrimes goto pasv_error; 292356668Sshin if (pasv_addr.su_family == AF_INET) 292456668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 292556668Sshin else if (pasv_addr.su_family == AF_INET6 && 292656668Sshin IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 292756668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 292856668Sshin else 292956668Sshin goto pasv_error; 293056668Sshin 293156668Sshin p = (char *) &pasv_addr.su_port; 29321592Srgrimes 29331592Srgrimes#define UC(b) (((int) b) & 0xff) 29341592Srgrimes 29351592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 29361592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 29371592Srgrimes return; 29381592Srgrimes 29391592Srgrimespasv_error: 2940132893Syar (void) seteuid(pw->pw_uid); 29411592Srgrimes (void) close(pdata); 29421592Srgrimes pdata = -1; 29431592Srgrimes perror_reply(425, "Can't open passive connection"); 29441592Srgrimes return; 29451592Srgrimes} 29461592Srgrimes 29471592Srgrimes/* 294856668Sshin * Long Passive defined in RFC 1639. 294956668Sshin * 228 Entering Long Passive Mode 295056668Sshin * (af, hal, h1, h2, h3,..., pal, p1, p2...) 295156668Sshin */ 295256668Sshin 295356668Sshinvoid 295490148Simplong_passive(char *cmd, int pf) 295556668Sshin{ 2956141918Sstefanf socklen_t len; 2957141918Sstefanf int on; 295856668Sshin char *p, *a; 295956668Sshin 296056668Sshin if (pdata >= 0) /* close old port if one set */ 296156668Sshin close(pdata); 296256668Sshin 296356668Sshin if (pf != PF_UNSPEC) { 296456668Sshin if (ctrl_addr.su_family != pf) { 296556668Sshin switch (ctrl_addr.su_family) { 296656668Sshin case AF_INET: 296756668Sshin pf = 1; 296856668Sshin break; 296956668Sshin case AF_INET6: 297056668Sshin pf = 2; 297156668Sshin break; 297256668Sshin default: 297356668Sshin pf = 0; 297456668Sshin break; 297556668Sshin } 297656668Sshin /* 297756668Sshin * XXX 297856668Sshin * only EPRT/EPSV ready clients will understand this 297956668Sshin */ 298056668Sshin if (strcmp(cmd, "EPSV") == 0 && pf) { 298156668Sshin reply(522, "Network protocol mismatch, " 298256668Sshin "use (%d)", pf); 298356668Sshin } else 2984137852Syar reply(501, "Network protocol mismatch."); /*XXX*/ 298556668Sshin 298656668Sshin return; 298756668Sshin } 298856668Sshin } 298956668Sshin 299056668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 299156668Sshin if (pdata < 0) { 299256668Sshin perror_reply(425, "Can't open passive connection"); 299356668Sshin return; 299456668Sshin } 2995100615Syar on = 1; 2996100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2997100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 299856668Sshin 2999132893Syar (void) seteuid(0); 300056668Sshin 300156668Sshin pasv_addr = ctrl_addr; 300256668Sshin pasv_addr.su_port = 0; 300356668Sshin len = pasv_addr.su_len; 300456668Sshin 300560929Snsayer#ifdef IP_PORTRANGE 300660929Snsayer if (ctrl_addr.su_family == AF_INET) { 3007100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 3008100615Syar : IP_PORTRANGE_DEFAULT; 300960929Snsayer 301060929Snsayer if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 3011100612Syar &on, sizeof(on)) < 0) 301260929Snsayer goto pasv_error; 301360929Snsayer } 301460929Snsayer#endif 301560929Snsayer#ifdef IPV6_PORTRANGE 301660929Snsayer if (ctrl_addr.su_family == AF_INET6) { 3017100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 3018100615Syar : IPV6_PORTRANGE_DEFAULT; 301960929Snsayer 302060929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 3021100612Syar &on, sizeof(on)) < 0) 302260929Snsayer goto pasv_error; 302360929Snsayer } 302460929Snsayer#endif 302560929Snsayer 302656668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 302756668Sshin goto pasv_error; 302856668Sshin 3029132893Syar (void) seteuid(pw->pw_uid); 303056668Sshin 303156668Sshin if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 303256668Sshin goto pasv_error; 303356668Sshin if (listen(pdata, 1) < 0) 303456668Sshin goto pasv_error; 303556668Sshin 303656668Sshin#define UC(b) (((int) b) & 0xff) 303756668Sshin 303856668Sshin if (strcmp(cmd, "LPSV") == 0) { 303956668Sshin p = (char *)&pasv_addr.su_port; 304056668Sshin switch (pasv_addr.su_family) { 304156668Sshin case AF_INET: 304256668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 304356668Sshin v4_reply: 304456668Sshin reply(228, 304556668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 304656668Sshin 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 304756668Sshin 2, UC(p[0]), UC(p[1])); 304856668Sshin return; 304956668Sshin case AF_INET6: 305056668Sshin if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 305156668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 305256668Sshin goto v4_reply; 305356668Sshin } 305456668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr; 305556668Sshin reply(228, 305656668Sshin"Entering Long Passive Mode " 305756668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 305856668Sshin 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 305956668Sshin UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 306056668Sshin UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 306156668Sshin UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 306256668Sshin 2, UC(p[0]), UC(p[1])); 306356668Sshin return; 306456668Sshin } 306556668Sshin } else if (strcmp(cmd, "EPSV") == 0) { 306656668Sshin switch (pasv_addr.su_family) { 306756668Sshin case AF_INET: 306856668Sshin case AF_INET6: 306956668Sshin reply(229, "Entering Extended Passive Mode (|||%d|)", 307056668Sshin ntohs(pasv_addr.su_port)); 307156668Sshin return; 307256668Sshin } 307356668Sshin } else { 307456668Sshin /* more proper error code? */ 307556668Sshin } 307656668Sshin 307756668Sshinpasv_error: 3078132893Syar (void) seteuid(pw->pw_uid); 307956668Sshin (void) close(pdata); 308056668Sshin pdata = -1; 308156668Sshin perror_reply(425, "Can't open passive connection"); 308256668Sshin return; 308356668Sshin} 308456668Sshin 308556668Sshin/* 3086101537Syar * Generate unique name for file with basename "local" 3087101537Syar * and open the file in order to avoid possible races. 3088101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 3089101537Syar * Return descriptor to the file, set "name" to its name. 3090101537Syar * 30911592Srgrimes * Generates failure reply on error. 30921592Srgrimes */ 3093101537Syarstatic int 3094101537Syarguniquefd(char *local, char **name) 30951592Srgrimes{ 30961592Srgrimes static char new[MAXPATHLEN]; 30971592Srgrimes struct stat st; 3098101537Syar char *cp; 30991592Srgrimes int count; 3100101537Syar int fd; 31011592Srgrimes 31021592Srgrimes cp = strrchr(local, '/'); 31031592Srgrimes if (cp) 31041592Srgrimes *cp = '\0'; 31051592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 31061592Srgrimes perror_reply(553, cp ? local : "."); 3107101537Syar return (-1); 31081592Srgrimes } 3109101537Syar if (cp) { 3110101537Syar /* 3111101537Syar * Let not overwrite dirname with counter suffix. 3112101537Syar * -4 is for /nn\0 3113101537Syar * In this extreme case dot won't be put in front of suffix. 3114101537Syar */ 3115101537Syar if (strlen(local) > sizeof(new) - 4) { 3116137852Syar reply(553, "Pathname too long."); 3117101537Syar return (-1); 3118101537Syar } 31191592Srgrimes *cp = '/'; 3120101537Syar } 312131973Simp /* -4 is for the .nn<null> we put on the end below */ 312231973Simp (void) snprintf(new, sizeof(new) - 4, "%s", local); 31231592Srgrimes cp = new + strlen(new); 3124101537Syar /* 3125101537Syar * Don't generate dotfile unless requested explicitly. 3126101537Syar * This covers the case when basename gets truncated off 3127101537Syar * by buffer size. 3128101537Syar */ 3129101537Syar if (cp > new && cp[-1] != '/') 3130101537Syar *cp++ = '.'; 3131101537Syar for (count = 0; count < 100; count++) { 3132101537Syar /* At count 0 try unmodified name */ 3133101537Syar if (count) 3134101537Syar (void)sprintf(cp, "%d", count); 3135101537Syar if ((fd = open(count ? new : local, 3136101537Syar O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 3137101537Syar *name = count ? new : local; 3138101537Syar return (fd); 3139101537Syar } 3140110046Syar if (errno != EEXIST) { 3141110307Syar perror_reply(553, count ? new : local); 3142110046Syar return (-1); 3143110046Syar } 31441592Srgrimes } 31451592Srgrimes reply(452, "Unique file name cannot be created."); 3146101537Syar return (-1); 31471592Srgrimes} 31481592Srgrimes 31491592Srgrimes/* 31501592Srgrimes * Format and send reply containing system error number. 31511592Srgrimes */ 31521592Srgrimesvoid 315390148Simpperror_reply(int code, char *string) 31541592Srgrimes{ 31551592Srgrimes 31561592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 31571592Srgrimes} 31581592Srgrimes 31591592Srgrimesstatic char *onefile[] = { 31601592Srgrimes "", 31611592Srgrimes 0 31621592Srgrimes}; 31631592Srgrimes 31641592Srgrimesvoid 316590148Simpsend_file_list(char *whichf) 31661592Srgrimes{ 31671592Srgrimes struct stat st; 31681592Srgrimes DIR *dirp = NULL; 31691592Srgrimes struct dirent *dir; 31701592Srgrimes FILE *dout = NULL; 31711592Srgrimes char **dirlist, *dirname; 31721592Srgrimes int simple = 0; 31731592Srgrimes int freeglob = 0; 31741592Srgrimes glob_t gl; 31751592Srgrimes 31761592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 3177100222Smikeh int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 31781592Srgrimes 31791592Srgrimes memset(&gl, 0, sizeof(gl)); 318074470Sjlemon gl.gl_matchc = MAXGLOBARGS; 318180525Smikeh flags |= GLOB_LIMIT; 31821592Srgrimes freeglob = 1; 31831592Srgrimes if (glob(whichf, flags, 0, &gl)) { 3184137852Syar reply(550, "No matching files found."); 31851592Srgrimes goto out; 31861592Srgrimes } else if (gl.gl_pathc == 0) { 31871592Srgrimes errno = ENOENT; 31881592Srgrimes perror_reply(550, whichf); 31891592Srgrimes goto out; 31901592Srgrimes } 31911592Srgrimes dirlist = gl.gl_pathv; 31921592Srgrimes } else { 31931592Srgrimes onefile[0] = whichf; 31941592Srgrimes dirlist = onefile; 31951592Srgrimes simple = 1; 31961592Srgrimes } 31971592Srgrimes 319817478Smarkm while ((dirname = *dirlist++)) { 31991592Srgrimes if (stat(dirname, &st) < 0) { 32001592Srgrimes /* 32011592Srgrimes * If user typed "ls -l", etc, and the client 32021592Srgrimes * used NLST, do what the user meant. 32031592Srgrimes */ 32041592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 3205140472Syar dout == NULL) 320625165Sdavidn retrieve(_PATH_LS " %s", dirname); 3207140472Syar else 3208140472Syar perror_reply(550, whichf); 32091592Srgrimes goto out; 32101592Srgrimes } 32111592Srgrimes 32121592Srgrimes if (S_ISREG(st.st_mode)) { 32131592Srgrimes if (dout == NULL) { 3214132930Syar dout = dataconn("file list", -1, "w"); 32151592Srgrimes if (dout == NULL) 32161592Srgrimes goto out; 3217140472Syar STARTXFER; 32181592Srgrimes } 3219140472Syar START_UNSAFE; 32201592Srgrimes fprintf(dout, "%s%s\n", dirname, 32211592Srgrimes type == TYPE_A ? "\r" : ""); 3222140472Syar END_UNSAFE; 3223140472Syar if (ferror(dout)) 3224140472Syar goto data_err; 3225140472Syar byte_count += strlen(dirname) + 3226140472Syar (type == TYPE_A ? 2 : 1); 3227140472Syar CHECKOOB(goto abrt); 32281592Srgrimes continue; 32291592Srgrimes } else if (!S_ISDIR(st.st_mode)) 32301592Srgrimes continue; 32311592Srgrimes 32321592Srgrimes if ((dirp = opendir(dirname)) == NULL) 32331592Srgrimes continue; 32341592Srgrimes 32351592Srgrimes while ((dir = readdir(dirp)) != NULL) { 32361592Srgrimes char nbuf[MAXPATHLEN]; 32371592Srgrimes 3238140472Syar CHECKOOB(goto abrt); 323989935Syar 32401592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 32411592Srgrimes continue; 32421592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 32431592Srgrimes dir->d_namlen == 2) 32441592Srgrimes continue; 32451592Srgrimes 324699213Smaxim snprintf(nbuf, sizeof(nbuf), 324731973Simp "%s/%s", dirname, dir->d_name); 32481592Srgrimes 32491592Srgrimes /* 32501592Srgrimes * We have to do a stat to insure it's 32511592Srgrimes * not a directory or special file. 32521592Srgrimes */ 32531592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 32541592Srgrimes S_ISREG(st.st_mode))) { 32551592Srgrimes if (dout == NULL) { 3256132930Syar dout = dataconn("file list", -1, "w"); 32571592Srgrimes if (dout == NULL) 32581592Srgrimes goto out; 3259140472Syar STARTXFER; 32601592Srgrimes } 3261140472Syar START_UNSAFE; 32621592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 32631592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 32641592Srgrimes type == TYPE_A ? "\r" : ""); 32651592Srgrimes else 32661592Srgrimes fprintf(dout, "%s%s\n", nbuf, 32671592Srgrimes type == TYPE_A ? "\r" : ""); 3268140472Syar END_UNSAFE; 3269140472Syar if (ferror(dout)) 3270140472Syar goto data_err; 3271140472Syar byte_count += strlen(nbuf) + 3272140472Syar (type == TYPE_A ? 2 : 1); 3273140472Syar CHECKOOB(goto abrt); 32741592Srgrimes } 32751592Srgrimes } 32761592Srgrimes (void) closedir(dirp); 3277140472Syar dirp = NULL; 32781592Srgrimes } 32791592Srgrimes 32801592Srgrimes if (dout == NULL) 32811592Srgrimes reply(550, "No files found."); 3282140472Syar else if (ferror(dout)) 3283140472Syardata_err: perror_reply(550, "Data connection"); 32841592Srgrimes else 32851592Srgrimes reply(226, "Transfer complete."); 3286140472Syarout: 3287140472Syar if (dout) { 3288140472Syar ENDXFER; 3289140472Syarabrt: 32901592Srgrimes (void) fclose(dout); 3291140472Syar data = -1; 3292140472Syar pdata = -1; 3293140472Syar } 3294140472Syar if (dirp) 3295140472Syar (void) closedir(dirp); 32961592Srgrimes if (freeglob) { 32971592Srgrimes freeglob = 0; 32981592Srgrimes globfree(&gl); 32991592Srgrimes } 33001592Srgrimes} 33011592Srgrimes 330215196Sdgvoid 330390148Simpreapchild(int signo) 330415196Sdg{ 3305137830Syar while (waitpid(-1, NULL, WNOHANG) > 0); 330615196Sdg} 330715196Sdg 330813139Speter#ifdef OLD_SETPROCTITLE 33091592Srgrimes/* 33101592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 33111592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 33121592Srgrimes * have much of an environment or arglist to overwrite. 33131592Srgrimes */ 33141592Srgrimesvoid 33151592Srgrimessetproctitle(const char *fmt, ...) 33161592Srgrimes{ 33171592Srgrimes int i; 33181592Srgrimes va_list ap; 33191592Srgrimes char *p, *bp, ch; 33201592Srgrimes char buf[LINE_MAX]; 33211592Srgrimes 33221592Srgrimes va_start(ap, fmt); 33231592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 33241592Srgrimes 33251592Srgrimes /* make ps print our process name */ 33261592Srgrimes p = Argv[0]; 33271592Srgrimes *p++ = '-'; 33281592Srgrimes 33291592Srgrimes i = strlen(buf); 33301592Srgrimes if (i > LastArgv - p - 2) { 33311592Srgrimes i = LastArgv - p - 2; 33321592Srgrimes buf[i] = '\0'; 33331592Srgrimes } 33341592Srgrimes bp = buf; 33351592Srgrimes while (ch = *bp++) 33361592Srgrimes if (ch != '\n' && ch != '\r') 33371592Srgrimes *p++ = ch; 33381592Srgrimes while (p < LastArgv) 33391592Srgrimes *p++ = ' '; 33401592Srgrimes} 334113139Speter#endif /* OLD_SETPROCTITLE */ 33426740Sguido 334317433Spststatic void 3344137848Syarappendf(char **strp, char *fmt, ...) 3345137848Syar{ 3346137848Syar va_list ap; 3347137848Syar char *ostr, *p; 3348137848Syar 3349137848Syar va_start(ap, fmt); 3350137848Syar vasprintf(&p, fmt, ap); 3351137848Syar va_end(ap); 3352137848Syar if (p == NULL) 3353137848Syar fatalerror("Ran out of memory."); 3354137848Syar if (*strp == NULL) 3355137848Syar *strp = p; 3356137848Syar else { 3357137848Syar ostr = *strp; 3358137848Syar asprintf(strp, "%s%s", ostr, p); 3359137848Syar if (*strp == NULL) 3360137848Syar fatalerror("Ran out of memory."); 3361137848Syar free(ostr); 3362137848Syar } 3363137848Syar} 3364137848Syar 3365137848Syarstatic void 3366137848Syarlogcmd(char *cmd, char *file1, char *file2, off_t cnt) 3367137848Syar{ 3368137848Syar char *msg = NULL; 3369137848Syar char wd[MAXPATHLEN + 1]; 3370137848Syar 3371137848Syar if (logging <= 1) 3372137848Syar return; 3373137848Syar 3374137985Syar if (getcwd(wd, sizeof(wd) - 1) == NULL) 3375137985Syar strcpy(wd, strerror(errno)); 3376137985Syar 3377137848Syar appendf(&msg, "%s", cmd); 3378137848Syar if (file1) 3379137848Syar appendf(&msg, " %s", file1); 3380137848Syar if (file2) 3381137848Syar appendf(&msg, " %s", file2); 3382137848Syar if (cnt >= 0) 3383137848Syar appendf(&msg, " = %jd bytes", (intmax_t)cnt); 3384137985Syar appendf(&msg, " (wd: %s", wd); 3385137862Syar if (guest || dochroot) 3386137985Syar appendf(&msg, "; chrooted"); 3387137985Syar appendf(&msg, ")"); 3388137848Syar syslog(LOG_INFO, "%s", msg); 3389137848Syar free(msg); 3390137848Syar} 3391137848Syar 3392137848Syarstatic void 339390148Simplogxfer(char *name, off_t size, time_t start) 33946740Sguido{ 3395137145Syar char buf[MAXPATHLEN + 1024]; 33966740Sguido char path[MAXPATHLEN + 1]; 339736612Sjb time_t now; 33986740Sguido 3399137145Syar if (statfd >= 0) { 34006740Sguido time(&now); 3401137145Syar if (realpath(name, path) == NULL) { 3402137145Syar syslog(LOG_NOTICE, "realpath failed on %s: %m", path); 3403137145Syar return; 3404137145Syar } 3405137145Syar snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", 34066740Sguido ctime(&now)+4, ident, remotehost, 3407137145Syar path, (intmax_t)size, 340882792Sache (long)(now - start + (now == start))); 34096740Sguido write(statfd, buf, strlen(buf)); 34106740Sguido } 34116740Sguido} 3412100486Syar 3413100486Syarstatic char * 3414100486Syardoublequote(char *s) 3415100486Syar{ 3416100486Syar int n; 3417100486Syar char *p, *s2; 3418100486Syar 3419100486Syar for (p = s, n = 0; *p; p++) 3420100486Syar if (*p == '"') 3421100486Syar n++; 3422100486Syar 3423100486Syar if ((s2 = malloc(p - s + n + 1)) == NULL) 3424100486Syar return (NULL); 3425100486Syar 3426100486Syar for (p = s2; *s; s++, p++) { 3427100486Syar if ((*p = *s) == '"') 3428100486Syar *(++p) = '"'; 3429100486Syar } 3430100486Syar *p = '\0'; 3431100486Syar 3432100486Syar return (s2); 3433100486Syar} 3434120059Sume 3435120059Sume/* setup server socket for specified address family */ 3436120059Sume/* if af is PF_UNSPEC more than one socket may be returned */ 3437120059Sume/* the returned list is dynamically allocated, so caller needs to free it */ 3438120059Sumestatic int * 3439120059Sumesocksetup(int af, char *bindname, const char *bindport) 3440120059Sume{ 3441120059Sume struct addrinfo hints, *res, *r; 3442120059Sume int error, maxs, *s, *socks; 3443120059Sume const int on = 1; 3444120059Sume 3445120059Sume memset(&hints, 0, sizeof(hints)); 3446120059Sume hints.ai_flags = AI_PASSIVE; 3447120059Sume hints.ai_family = af; 3448120059Sume hints.ai_socktype = SOCK_STREAM; 3449120059Sume error = getaddrinfo(bindname, bindport, &hints, &res); 3450120059Sume if (error) { 3451120059Sume syslog(LOG_ERR, "%s", gai_strerror(error)); 3452120059Sume if (error == EAI_SYSTEM) 3453120059Sume syslog(LOG_ERR, "%s", strerror(errno)); 3454120059Sume return NULL; 3455120059Sume } 3456120059Sume 3457120059Sume /* Count max number of sockets we may open */ 3458120059Sume for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 3459120059Sume ; 3460120059Sume socks = malloc((maxs + 1) * sizeof(int)); 3461120059Sume if (!socks) { 3462120059Sume freeaddrinfo(res); 3463120059Sume syslog(LOG_ERR, "couldn't allocate memory for sockets"); 3464120059Sume return NULL; 3465120059Sume } 3466120059Sume 3467120059Sume *socks = 0; /* num of sockets counter at start of array */ 3468120059Sume s = socks + 1; 3469120059Sume for (r = res; r; r = r->ai_next) { 3470120059Sume *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 3471120059Sume if (*s < 0) { 3472120059Sume syslog(LOG_DEBUG, "control socket: %m"); 3473120059Sume continue; 3474120059Sume } 3475120059Sume if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 3476120059Sume &on, sizeof(on)) < 0) 3477120059Sume syslog(LOG_WARNING, 3478120059Sume "control setsockopt (SO_REUSEADDR): %m"); 3479120059Sume if (r->ai_family == AF_INET6) { 3480120059Sume if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 3481120059Sume &on, sizeof(on)) < 0) 3482120059Sume syslog(LOG_WARNING, 3483120059Sume "control setsockopt (IPV6_V6ONLY): %m"); 3484120059Sume } 3485120059Sume if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 3486120059Sume syslog(LOG_DEBUG, "control bind: %m"); 3487120059Sume close(*s); 3488120059Sume continue; 3489120059Sume } 3490120059Sume (*socks)++; 3491120059Sume s++; 3492120059Sume } 3493120059Sume 3494120059Sume if (res) 3495120059Sume freeaddrinfo(res); 3496120059Sume 3497120059Sume if (*socks == 0) { 3498120059Sume syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); 3499120059Sume free(socks); 3500120059Sume return NULL; 3501120059Sume } 3502120059Sume return(socks); 3503120059Sume} 3504