ftpd.c revision 102183
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. 131592Srgrimes * 3. All advertising materials mentioning features or use of this software 141592Srgrimes * must display the following acknowledgement: 151592Srgrimes * This product includes software developed by the University of 161592Srgrimes * California, Berkeley and its contributors. 171592Srgrimes * 4. Neither the name of the University nor the names of its contributors 181592Srgrimes * may be used to endorse or promote products derived from this software 191592Srgrimes * without specific prior written permission. 201592Srgrimes * 211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311592Srgrimes * SUCH DAMAGE. 321592Srgrimes */ 331592Srgrimes 3417478Smarkm#if 0 351592Srgrimes#ifndef lint 361592Srgrimesstatic char copyright[] = 371592Srgrimes"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 381592Srgrimes The Regents of the University of California. All rights reserved.\n"; 391592Srgrimes#endif /* not lint */ 4017478Smarkm#endif 411592Srgrimes 4231329Scharnier#ifndef lint 4317478Smarkm#if 0 441592Srgrimesstatic char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; 4531329Scharnier#endif 4631329Scharnierstatic const char rcsid[] = 4750476Speter "$FreeBSD: head/libexec/ftpd/ftpd.c 102183 2002-08-20 14:56:06Z yar $"; 481592Srgrimes#endif /* not lint */ 491592Srgrimes 501592Srgrimes/* 511592Srgrimes * FTP server. 521592Srgrimes */ 531592Srgrimes#include <sys/param.h> 541592Srgrimes#include <sys/ioctl.h> 5566907Swollman#include <sys/mman.h> 561592Srgrimes#include <sys/socket.h> 5766907Swollman#include <sys/stat.h> 5866907Swollman#include <sys/time.h> 591592Srgrimes#include <sys/wait.h> 601592Srgrimes 611592Srgrimes#include <netinet/in.h> 621592Srgrimes#include <netinet/in_systm.h> 631592Srgrimes#include <netinet/ip.h> 648240Swollman#include <netinet/tcp.h> 651592Srgrimes 661592Srgrimes#define FTP_NAMES 671592Srgrimes#include <arpa/ftp.h> 681592Srgrimes#include <arpa/inet.h> 691592Srgrimes#include <arpa/telnet.h> 701592Srgrimes 711592Srgrimes#include <ctype.h> 721592Srgrimes#include <dirent.h> 731592Srgrimes#include <err.h> 741592Srgrimes#include <errno.h> 751592Srgrimes#include <fcntl.h> 761592Srgrimes#include <glob.h> 771592Srgrimes#include <limits.h> 781592Srgrimes#include <netdb.h> 791592Srgrimes#include <pwd.h> 8025187Sdavidn#include <grp.h> 8188763Sache#include <opie.h> 821592Srgrimes#include <signal.h> 831592Srgrimes#include <stdio.h> 841592Srgrimes#include <stdlib.h> 851592Srgrimes#include <string.h> 861592Srgrimes#include <syslog.h> 871592Srgrimes#include <time.h> 881592Srgrimes#include <unistd.h> 8913139Speter#include <libutil.h> 9025101Sdavidn#ifdef LOGIN_CAP 9125101Sdavidn#include <login_cap.h> 9225101Sdavidn#endif 931592Srgrimes 9474874Smarkm#ifdef USE_PAM 9551433Smarkm#include <security/pam_appl.h> 9651433Smarkm#endif 9751433Smarkm 981592Srgrimes#include "pathnames.h" 991592Srgrimes#include "extern.h" 1001592Srgrimes 1011592Srgrimes#include <stdarg.h> 1021592Srgrimes 10325165Sdavidnstatic char version[] = "Version 6.00LS"; 10425165Sdavidn#undef main 1051592Srgrimes 1061592Srgrimesextern off_t restart_point; 1071592Srgrimesextern char cbuf[]; 1081592Srgrimes 10956668Sshinunion sockunion server_addr; 11056668Sshinunion sockunion ctrl_addr; 11156668Sshinunion sockunion data_source; 11256668Sshinunion sockunion data_dest; 11356668Sshinunion sockunion his_addr; 11456668Sshinunion sockunion pasv_addr; 1151592Srgrimes 11615196Sdgint daemon_mode; 1171592Srgrimesint data; 1181592Srgrimesint logged_in; 1191592Srgrimesstruct passwd *pw; 12076096Smarkmint ftpdebug; 1211592Srgrimesint timeout = 900; /* timeout after 15 minutes of inactivity */ 1221592Srgrimesint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 1231592Srgrimesint logging; 1249933Spstint restricted_data_ports = 1; 12517435Spstint paranoid = 1; /* be extra careful about security */ 12620042Storstenbint anon_only = 0; /* Only anonymous ftp allowed */ 1271592Srgrimesint guest; 12817435Spstint dochroot; 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 */ 13770102Sphkint readonly=0; /* Server is in readonly mode. */ 13870102Sphkint noepsv=0; /* EPSV command is disabled. */ 13982460Snikint noretr=0; /* RETR command is disabled. */ 14082796Ssheldonhint noguestretr=0; /* RETR command is disabled for anon users. */ 14199195Smdoddint noguestmkd=0; /* MKD command is disabled for anon users. */ 142101537Syarint noguestmod=1; /* anon users may not modify existing files. */ 14382460Snik 14489935Syarstatic volatile sig_atomic_t recvurg; 1451592Srgrimessig_atomic_t transflag; 1461592Srgrimesoff_t file_size; 1471592Srgrimesoff_t byte_count; 1481592Srgrimes#if !defined(CMASK) || CMASK == 0 1491592Srgrimes#undef CMASK 1501592Srgrimes#define CMASK 027 1511592Srgrimes#endif 1521592Srgrimesint defumask = CMASK; /* default umask value */ 1531592Srgrimeschar tmpline[7]; 15427650Sdavidnchar *hostname; 15578153Sddint epsvall = 0; 15678153Sdd 15725283Sdavidn#ifdef VIRTUAL_HOSTING 15825283Sdavidnchar *ftpuser; 15925283Sdavidn 16025283Sdavidnstatic struct ftphost { 16125283Sdavidn struct ftphost *next; 16257124Sshin struct addrinfo *hostinfo; 16325283Sdavidn char *hostname; 16425283Sdavidn char *anonuser; 16525283Sdavidn char *statfile; 16625283Sdavidn char *welcome; 16725283Sdavidn char *loginmsg; 16825283Sdavidn} *thishost, *firsthost; 16925283Sdavidn 17025283Sdavidn#endif 17145422Sbrianchar remotehost[MAXHOSTNAMELEN]; 1726740Sguidochar *ident = NULL; 17317435Spst 17417435Spststatic char ttyline[20]; 17517435Spstchar *tty = ttyline; /* for klogin */ 17617435Spst 17774874Smarkm#ifdef USE_PAM 17890148Simpstatic int auth_pam(struct passwd**, const char*); 17974874Smarkmpam_handle_t *pamh = NULL; 18088763Sache#endif 18179469Smarkm 18279469Smarkmstatic struct opie opiedata; 18379469Smarkmstatic char opieprompt[OPIE_CHALLENGE_MAX+1]; 18488763Sachestatic int pwok; 18517478Smarkm 18617483Sjulianchar *pid_file = NULL; 18717483Sjulian 1881592Srgrimes/* 18974470Sjlemon * Limit number of pathnames that glob can return. 19074470Sjlemon * A limit of 0 indicates the number of pathnames is unlimited. 19174470Sjlemon */ 19274470Sjlemon#define MAXGLOBARGS 16384 19374470Sjlemon# 19474470Sjlemon 19574470Sjlemon/* 1961592Srgrimes * Timeout intervals for retrying connections 1971592Srgrimes * to hosts that don't accept PORT cmds. This 1981592Srgrimes * is a kludge, but given the problems with TCP... 1991592Srgrimes */ 2001592Srgrimes#define SWAITMAX 90 /* wait at most 90 seconds */ 2011592Srgrimes#define SWAITINT 5 /* interval between retries */ 2021592Srgrimes 2031592Srgrimesint swaitmax = SWAITMAX; 2041592Srgrimesint swaitint = SWAITINT; 2051592Srgrimes 2061592Srgrimes#ifdef SETPROCTITLE 20713139Speter#ifdef OLD_SETPROCTITLE 2081592Srgrimeschar **Argv = NULL; /* pointer to argument vector */ 2091592Srgrimeschar *LastArgv = NULL; /* end of argv */ 21013139Speter#endif /* OLD_SETPROCTITLE */ 2111592Srgrimeschar proctitle[LINE_MAX]; /* initial part of title */ 2121592Srgrimes#endif /* SETPROCTITLE */ 2131592Srgrimes 2141592Srgrimes#define LOGCMD(cmd, file) \ 2151592Srgrimes if (logging > 1) \ 2161592Srgrimes syslog(LOG_INFO,"%s %s%s", cmd, \ 2171592Srgrimes *(file) == '/' ? "" : curdir(), file); 2181592Srgrimes#define LOGCMD2(cmd, file1, file2) \ 2191592Srgrimes if (logging > 1) \ 2201592Srgrimes syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 2211592Srgrimes *(file1) == '/' ? "" : curdir(), file1, \ 2221592Srgrimes *(file2) == '/' ? "" : curdir(), file2); 2231592Srgrimes#define LOGBYTES(cmd, file, cnt) \ 2241592Srgrimes if (logging > 1) { \ 2251592Srgrimes if (cnt == (off_t)-1) \ 2261592Srgrimes syslog(LOG_INFO,"%s %s%s", cmd, \ 2271592Srgrimes *(file) == '/' ? "" : curdir(), file); \ 2281592Srgrimes else \ 2291592Srgrimes syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 2301592Srgrimes cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 2311592Srgrimes } 2321592Srgrimes 23325283Sdavidn#ifdef VIRTUAL_HOSTING 23490148Simpstatic void inithosts(void); 23590148Simpstatic void selecthost(union sockunion *); 23625283Sdavidn#endif 23790148Simpstatic void ack(char *); 23890148Simpstatic void sigurg(int); 23990148Simpstatic void myoob(void); 24090148Simpstatic int checkuser(char *, char *, int); 24190148Simpstatic FILE *dataconn(char *, off_t, char *); 24290148Simpstatic void dolog(struct sockaddr *); 24390148Simpstatic char *curdir(void); 24490148Simpstatic void end_login(void); 24590148Simpstatic FILE *getdatasock(char *); 246101537Syarstatic int guniquefd(char *, char **); 24790148Simpstatic void lostconn(int); 24890148Simpstatic void sigquit(int); 24990148Simpstatic int receive_data(FILE *, FILE *); 25090148Simpstatic int send_data(FILE *, FILE *, off_t, off_t, int); 2511592Srgrimesstatic struct passwd * 25290148Simp sgetpwnam(char *); 25390148Simpstatic char *sgetsave(char *); 25490148Simpstatic void reapchild(int); 25590148Simpstatic void logxfer(char *, off_t, time_t); 256100486Syarstatic char *doublequote(char *); 2571592Srgrimes 2581592Srgrimesstatic char * 25990148Simpcurdir(void) 2601592Srgrimes{ 2611592Srgrimes static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 2621592Srgrimes 2631592Srgrimes if (getcwd(path, sizeof(path)-2) == NULL) 2641592Srgrimes return (""); 2651592Srgrimes if (path[1] != '\0') /* special case for root dir. */ 2661592Srgrimes strcat(path, "/"); 2671592Srgrimes /* For guest account, skip / since it's chrooted */ 2681592Srgrimes return (guest ? path+1 : path); 2691592Srgrimes} 2701592Srgrimes 2711592Srgrimesint 27290148Simpmain(int argc, char *argv[], char **envp) 2731592Srgrimes{ 2741592Srgrimes int addrlen, ch, on = 1, tos; 2751592Srgrimes char *cp, line[LINE_MAX]; 2761592Srgrimes FILE *fd; 27756668Sshin int error; 27856668Sshin char *bindname = NULL; 27956668Sshin int family = AF_UNSPEC; 28056668Sshin int enable_v4 = 0; 28189935Syar struct sigaction sa; 2821592Srgrimes 28336105Sache tzset(); /* in case no timezone database in ~ftp */ 28489935Syar sigemptyset(&sa.sa_mask); 28589935Syar sa.sa_flags = SA_RESTART; 28636105Sache 28713139Speter#ifdef OLD_SETPROCTITLE 2881592Srgrimes /* 2891592Srgrimes * Save start and extent of argv for setproctitle. 2901592Srgrimes */ 2911592Srgrimes Argv = argv; 2921592Srgrimes while (*envp) 2931592Srgrimes envp++; 2941592Srgrimes LastArgv = envp[-1] + strlen(envp[-1]); 29513139Speter#endif /* OLD_SETPROCTITLE */ 2961592Srgrimes 2976740Sguido 298101537Syar while ((ch = getopt(argc, argv, "46a:AdDElmMoOp:rRSt:T:u:Uv")) != -1) { 2991592Srgrimes switch (ch) { 300100717Syar case '4': 301100717Syar enable_v4 = 1; 302100717Syar if (family == AF_UNSPEC) 303100717Syar family = AF_INET; 30415196Sdg break; 30515196Sdg 306100717Syar case '6': 307100717Syar family = AF_INET6; 308100717Syar break; 309100717Syar 310100717Syar case 'a': 311100717Syar bindname = optarg; 312100717Syar break; 313100717Syar 314100717Syar case 'A': 315100717Syar anon_only = 1; 316100717Syar break; 317100717Syar 3181592Srgrimes case 'd': 31976096Smarkm ftpdebug++; 3201592Srgrimes break; 3211592Srgrimes 322100717Syar case 'D': 323100717Syar daemon_mode++; 324100717Syar break; 325100717Syar 32670102Sphk case 'E': 32770102Sphk noepsv = 1; 32870102Sphk break; 32970102Sphk 3301592Srgrimes case 'l': 3311592Srgrimes logging++; /* > 1 == extra logging */ 3321592Srgrimes break; 3331592Srgrimes 334101537Syar case 'm': 335101537Syar noguestmod = 0; 336101537Syar break; 337101537Syar 338100717Syar case 'M': 339100717Syar noguestmkd = 1; 340100717Syar break; 341100717Syar 342100717Syar case 'o': 343100717Syar noretr = 1; 344100717Syar break; 345100717Syar 346100717Syar case 'O': 347100717Syar noguestretr = 1; 348100717Syar break; 349100717Syar 350100717Syar case 'p': 351100717Syar pid_file = optarg; 352100717Syar break; 353100717Syar 35470102Sphk case 'r': 35570102Sphk readonly = 1; 35670102Sphk break; 35770102Sphk 35817435Spst case 'R': 35917435Spst paranoid = 0; 3609933Spst break; 3619933Spst 3626740Sguido case 'S': 36317435Spst stats++; 3646740Sguido break; 36517435Spst 36617435Spst case 't': 36717435Spst timeout = atoi(optarg); 36817435Spst if (maxtimeout < timeout) 36917435Spst maxtimeout = timeout; 37017435Spst break; 37117435Spst 372100717Syar case 'T': 373100717Syar maxtimeout = atoi(optarg); 374100717Syar if (timeout > maxtimeout) 375100717Syar timeout = maxtimeout; 37617435Spst break; 37717435Spst 3781592Srgrimes case 'u': 3791592Srgrimes { 3801592Srgrimes long val = 0; 3811592Srgrimes 3821592Srgrimes val = strtol(optarg, &optarg, 8); 3831592Srgrimes if (*optarg != '\0' || val < 0) 3841592Srgrimes warnx("bad value for -u"); 3851592Srgrimes else 3861592Srgrimes defumask = val; 3871592Srgrimes break; 3881592Srgrimes } 389100717Syar case 'U': 390100717Syar restricted_data_ports = 0; 39120042Storstenb break; 3921592Srgrimes 3931592Srgrimes case 'v': 394100720Syar ftpdebug++; 3951592Srgrimes break; 3961592Srgrimes 3971592Srgrimes default: 3981592Srgrimes warnx("unknown flag -%c ignored", optopt); 3991592Srgrimes break; 4001592Srgrimes } 4011592Srgrimes } 40215196Sdg 40325283Sdavidn#ifdef VIRTUAL_HOSTING 40425283Sdavidn inithosts(); 40525283Sdavidn#endif 4061592Srgrimes (void) freopen(_PATH_DEVNULL, "w", stderr); 40715196Sdg 40815196Sdg /* 40915196Sdg * LOG_NDELAY sets up the logging connection immediately, 41015196Sdg * necessary for anonymous ftp's that chroot and can't do it later. 41115196Sdg */ 41215196Sdg openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 41315196Sdg 41415196Sdg if (daemon_mode) { 41515196Sdg int ctl_sock, fd; 41656668Sshin struct addrinfo hints, *res; 41715196Sdg 41815196Sdg /* 41915196Sdg * Detach from parent. 42015196Sdg */ 42115196Sdg if (daemon(1, 1) < 0) { 42215196Sdg syslog(LOG_ERR, "failed to become a daemon"); 42315196Sdg exit(1); 42415196Sdg } 42589935Syar sa.sa_handler = reapchild; 42689935Syar (void)sigaction(SIGCHLD, &sa, NULL); 42756668Sshin /* init bind_sa */ 42856668Sshin memset(&hints, 0, sizeof(hints)); 42956668Sshin 43056668Sshin hints.ai_family = family == AF_UNSPEC ? AF_INET : family; 43156668Sshin hints.ai_socktype = SOCK_STREAM; 43256668Sshin hints.ai_protocol = 0; 43356668Sshin hints.ai_flags = AI_PASSIVE; 43456668Sshin error = getaddrinfo(bindname, "ftp", &hints, &res); 43556668Sshin if (error) { 43656668Sshin if (family == AF_UNSPEC) { 43756668Sshin hints.ai_family = AF_UNSPEC; 43856668Sshin error = getaddrinfo(bindname, "ftp", &hints, 43956668Sshin &res); 44056668Sshin } 44156668Sshin } 44256668Sshin if (error) { 44368901Skris syslog(LOG_ERR, "%s", gai_strerror(error)); 44456668Sshin if (error == EAI_SYSTEM) 44568901Skris syslog(LOG_ERR, "%s", strerror(errno)); 44615196Sdg exit(1); 44715196Sdg } 44856668Sshin if (res->ai_addr == NULL) { 44956668Sshin syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); 45056668Sshin exit(1); 45164699Sru } else 45264699Sru family = res->ai_addr->sa_family; 45315196Sdg /* 45415196Sdg * Open a socket, bind it to the FTP port, and start 45515196Sdg * listening. 45615196Sdg */ 45756668Sshin ctl_sock = socket(family, SOCK_STREAM, 0); 45815196Sdg if (ctl_sock < 0) { 45915196Sdg syslog(LOG_ERR, "control socket: %m"); 46015196Sdg exit(1); 46115196Sdg } 46215196Sdg if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, 463100612Syar &on, sizeof(on)) < 0) 464100609Syar syslog(LOG_WARNING, 465100609Syar "control setsockopt (SO_REUSEADDR): %m"); 46656668Sshin if (family == AF_INET6 && enable_v4 == 0) { 467100505Sume if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_V6ONLY, 468100612Syar &on, sizeof (on)) < 0) 469100609Syar syslog(LOG_WARNING, 470100609Syar "control setsockopt (IPV6_V6ONLY): %m"); 47156668Sshin } 47256668Sshin memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len); 47356668Sshin if (bind(ctl_sock, (struct sockaddr *)&server_addr, 47456668Sshin server_addr.su_len) < 0) { 47515196Sdg syslog(LOG_ERR, "control bind: %m"); 47615196Sdg exit(1); 47715196Sdg } 47815196Sdg if (listen(ctl_sock, 32) < 0) { 47915196Sdg syslog(LOG_ERR, "control listen: %m"); 48015196Sdg exit(1); 48115196Sdg } 48215196Sdg /* 48317483Sjulian * Atomically write process ID 48417483Sjulian */ 48517483Sjulian if (pid_file) 48699213Smaxim { 48717483Sjulian int fd; 48817483Sjulian char buf[20]; 48917483Sjulian 49017483Sjulian fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC 49117483Sjulian | O_NONBLOCK | O_EXLOCK, 0644); 49246078Simp if (fd < 0) { 49317483Sjulian if (errno == EAGAIN) 49417483Sjulian errx(1, "%s: file locked", pid_file); 49517483Sjulian else 49617483Sjulian err(1, "%s", pid_file); 49746078Simp } 49817483Sjulian snprintf(buf, sizeof(buf), 49917483Sjulian "%lu\n", (unsigned long) getpid()); 50017483Sjulian if (write(fd, buf, strlen(buf)) < 0) 50117483Sjulian err(1, "%s: write", pid_file); 50217483Sjulian /* Leave the pid file open and locked */ 50317483Sjulian } 50417483Sjulian /* 50515196Sdg * Loop forever accepting connection requests and forking off 50615196Sdg * children to handle them. 50715196Sdg */ 50815196Sdg while (1) { 50956668Sshin addrlen = server_addr.su_len; 51015196Sdg fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); 51115196Sdg if (fork() == 0) { 51215196Sdg /* child */ 51315196Sdg (void) dup2(fd, 0); 51415196Sdg (void) dup2(fd, 1); 51515196Sdg close(ctl_sock); 51615196Sdg break; 51715196Sdg } 51815196Sdg close(fd); 51915196Sdg } 52015196Sdg } else { 52115196Sdg addrlen = sizeof(his_addr); 52215196Sdg if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 52315196Sdg syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 52415196Sdg exit(1); 52515196Sdg } 52615196Sdg } 52715196Sdg 52889935Syar sa.sa_handler = SIG_DFL; 52989935Syar (void)sigaction(SIGCHLD, &sa, NULL); 5301592Srgrimes 53189935Syar sa.sa_handler = sigurg; 53289935Syar sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ 53389935Syar (void)sigaction(SIGURG, &sa, NULL); 53489935Syar 53589935Syar sigfillset(&sa.sa_mask); /* block all signals in handler */ 53689935Syar sa.sa_flags = SA_RESTART; 53789935Syar sa.sa_handler = sigquit; 53889935Syar (void)sigaction(SIGHUP, &sa, NULL); 53989935Syar (void)sigaction(SIGINT, &sa, NULL); 54089935Syar (void)sigaction(SIGQUIT, &sa, NULL); 54189935Syar (void)sigaction(SIGTERM, &sa, NULL); 54289935Syar 54389935Syar sa.sa_handler = lostconn; 54489935Syar (void)sigaction(SIGPIPE, &sa, NULL); 54589935Syar 54615196Sdg addrlen = sizeof(ctrl_addr); 54715196Sdg if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 54815196Sdg syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 54915196Sdg exit(1); 55015196Sdg } 55125283Sdavidn#ifdef VIRTUAL_HOSTING 55225283Sdavidn /* select our identity from virtual host table */ 55356668Sshin selecthost(&ctrl_addr); 55425283Sdavidn#endif 55515196Sdg#ifdef IP_TOS 55656668Sshin if (ctrl_addr.su_family == AF_INET) 55756668Sshin { 55815196Sdg tos = IPTOS_LOWDELAY; 559100612Syar if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 560100609Syar syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); 56156668Sshin } 56215196Sdg#endif 56335482Sdg /* 56435482Sdg * Disable Nagle on the control channel so that we don't have to wait 56535482Sdg * for peer's ACK before issuing our next reply. 56635482Sdg */ 56735482Sdg if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 568100609Syar syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); 56935482Sdg 57056668Sshin data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); 57115196Sdg 57217435Spst /* set this here so klogin can use it... */ 57331973Simp (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); 57417435Spst 5751592Srgrimes /* Try to handle urgent data inline */ 5761592Srgrimes#ifdef SO_OOBINLINE 577100612Syar if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) 578100609Syar syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); 5791592Srgrimes#endif 5801592Srgrimes 5811592Srgrimes#ifdef F_SETOWN 5821592Srgrimes if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 5831592Srgrimes syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 5841592Srgrimes#endif 58556668Sshin dolog((struct sockaddr *)&his_addr); 5861592Srgrimes /* 5871592Srgrimes * Set up default state 5881592Srgrimes */ 5891592Srgrimes data = -1; 5901592Srgrimes type = TYPE_A; 5911592Srgrimes form = FORM_N; 5921592Srgrimes stru = STRU_F; 5931592Srgrimes mode = MODE_S; 5941592Srgrimes tmpline[0] = '\0'; 5951592Srgrimes 5961592Srgrimes /* If logins are disabled, print out the message. */ 5971592Srgrimes if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 5981592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 5991592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6001592Srgrimes *cp = '\0'; 6011592Srgrimes lreply(530, "%s", line); 6021592Srgrimes } 6031592Srgrimes (void) fflush(stdout); 6041592Srgrimes (void) fclose(fd); 6051592Srgrimes reply(530, "System not available."); 6061592Srgrimes exit(0); 6071592Srgrimes } 60825283Sdavidn#ifdef VIRTUAL_HOSTING 60925283Sdavidn if ((fd = fopen(thishost->welcome, "r")) != NULL) { 61025283Sdavidn#else 6111592Srgrimes if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 61225283Sdavidn#endif 6131592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6141592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6151592Srgrimes *cp = '\0'; 6161592Srgrimes lreply(220, "%s", line); 6171592Srgrimes } 6181592Srgrimes (void) fflush(stdout); 6191592Srgrimes (void) fclose(fd); 6201592Srgrimes /* reply(220,) must follow */ 6211592Srgrimes } 62225283Sdavidn#ifndef VIRTUAL_HOSTING 62327650Sdavidn if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 62476096Smarkm fatalerror("Ran out of memory."); 62545422Sbrian (void) gethostname(hostname, MAXHOSTNAMELEN - 1); 62645422Sbrian hostname[MAXHOSTNAMELEN - 1] = '\0'; 62725283Sdavidn#endif 6281592Srgrimes reply(220, "%s FTP server (%s) ready.", hostname, version); 6291592Srgrimes for (;;) 6301592Srgrimes (void) yyparse(); 6311592Srgrimes /* NOTREACHED */ 6321592Srgrimes} 6331592Srgrimes 6341592Srgrimesstatic void 63590148Simplostconn(int signo) 6361592Srgrimes{ 6371592Srgrimes 63876096Smarkm if (ftpdebug) 6391592Srgrimes syslog(LOG_DEBUG, "lost connection"); 64031329Scharnier dologout(1); 6411592Srgrimes} 6421592Srgrimes 64389935Syarstatic void 64490148Simpsigquit(int signo) 64589935Syar{ 64689935Syar 64789935Syar syslog(LOG_ERR, "got signal %d", signo); 64889935Syar dologout(1); 64989935Syar} 65089935Syar 65125283Sdavidn#ifdef VIRTUAL_HOSTING 6521592Srgrimes/* 65325283Sdavidn * read in virtual host tables (if they exist) 65425283Sdavidn */ 65525283Sdavidn 65625283Sdavidnstatic void 65790148Simpinithosts(void) 65825283Sdavidn{ 659100182Syar int insert; 66099877Syar size_t len; 66125283Sdavidn FILE *fp; 66299877Syar char *cp, *mp, *line; 66399877Syar char *hostname; 664100182Syar char *vhost, *anonuser, *statfile, *welcome, *loginmsg; 66525283Sdavidn struct ftphost *hrp, *lhrp; 66656668Sshin struct addrinfo hints, *res, *ai; 66725283Sdavidn 66825283Sdavidn /* 66925283Sdavidn * Fill in the default host information 67025283Sdavidn */ 67199877Syar if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 67276096Smarkm fatalerror("Ran out of memory."); 67399877Syar if (gethostname(hostname, MAXHOSTNAMELEN) < 0) 67499877Syar hostname[0] = '\0'; 67599877Syar hostname[MAXHOSTNAMELEN - 1] = '\0'; 67699877Syar if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 67799877Syar fatalerror("Ran out of memory."); 67899877Syar hrp->hostname = hostname; 67957124Sshin hrp->hostinfo = NULL; 68056668Sshin 68156668Sshin memset(&hints, 0, sizeof(hints)); 68256668Sshin hints.ai_flags = AI_CANONNAME; 68356668Sshin hints.ai_family = AF_UNSPEC; 684102183Syar if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) 68557124Sshin hrp->hostinfo = res; 68625283Sdavidn hrp->statfile = _PATH_FTPDSTATFILE; 68725283Sdavidn hrp->welcome = _PATH_FTPWELCOME; 68825283Sdavidn hrp->loginmsg = _PATH_FTPLOGINMESG; 68925283Sdavidn hrp->anonuser = "ftp"; 69025283Sdavidn hrp->next = NULL; 69125283Sdavidn thishost = firsthost = lhrp = hrp; 69225283Sdavidn if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { 69362100Sdavidn int addrsize, error, gothost; 69456668Sshin void *addr; 69556668Sshin struct hostent *hp; 69656668Sshin 69799877Syar while ((line = fgetln(fp, &len)) != NULL) { 69856668Sshin int i, hp_error; 69925283Sdavidn 70099877Syar /* skip comments */ 70199877Syar if (line[0] == '#') 70225283Sdavidn continue; 70399877Syar if (line[len - 1] == '\n') { 70499877Syar line[len - 1] = '\0'; 70599877Syar mp = NULL; 70699877Syar } else { 70799877Syar if ((mp = malloc(len + 1)) == NULL) 70899877Syar fatalerror("Ran out of memory."); 70999877Syar memcpy(mp, line, len); 71099877Syar mp[len] = '\0'; 71199877Syar line = mp; 71225283Sdavidn } 71325283Sdavidn cp = strtok(line, " \t"); 71499877Syar /* skip empty lines */ 71599877Syar if (cp == NULL) 71699877Syar goto nextline; 717100182Syar vhost = cp; 71856668Sshin 719100182Syar /* set defaults */ 720100182Syar anonuser = "ftp"; 721100182Syar statfile = _PATH_FTPDSTATFILE; 722100182Syar welcome = _PATH_FTPWELCOME; 723100182Syar loginmsg = _PATH_FTPLOGINMESG; 724100182Syar 725100182Syar /* 726100182Syar * Preparse the line so we can use its info 727100182Syar * for all the addresses associated with 728100182Syar * the virtual host name. 729100182Syar * Field 0, the virtual host name, is special: 730100182Syar * it's already parsed off and will be strdup'ed 731100182Syar * later, after we know its canonical form. 732100182Syar */ 733100182Syar for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) 734100182Syar if (*cp != '-' && (cp = strdup(cp))) 735100182Syar switch (i) { 736100182Syar case 1: /* anon user permissions */ 737100182Syar anonuser = cp; 738100182Syar break; 739100182Syar case 2: /* statistics file */ 740100182Syar statfile = cp; 741100182Syar break; 742100182Syar case 3: /* welcome message */ 743100182Syar welcome = cp; 744100182Syar break; 745100182Syar case 4: /* login message */ 746100182Syar loginmsg = cp; 747100182Syar break; 748100182Syar default: /* programming error */ 749100182Syar abort(); 750100182Syar /* NOTREACHED */ 751100182Syar } 752100182Syar 75356668Sshin hints.ai_flags = 0; 75456668Sshin hints.ai_family = AF_UNSPEC; 75556668Sshin hints.ai_flags = AI_PASSIVE; 756102183Syar if (getaddrinfo(vhost, NULL, &hints, &res) != 0) 75799877Syar goto nextline; 75856668Sshin for (ai = res; ai != NULL && ai->ai_addr != NULL; 75962100Sdavidn ai = ai->ai_next) { 76056668Sshin 76162100Sdavidn gothost = 0; 76225283Sdavidn for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { 76357124Sshin struct addrinfo *hi; 76457124Sshin 76557124Sshin for (hi = hrp->hostinfo; hi != NULL; 76657124Sshin hi = hi->ai_next) 76757124Sshin if (hi->ai_addrlen == ai->ai_addrlen && 76857124Sshin memcmp(hi->ai_addr, 76957124Sshin ai->ai_addr, 77062100Sdavidn ai->ai_addr->sa_len) == 0) { 77162100Sdavidn gothost++; 77257124Sshin break; 773100183Syar } 77462100Sdavidn if (gothost) 77562100Sdavidn break; 77625283Sdavidn } 77725283Sdavidn if (hrp == NULL) { 77825283Sdavidn if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 77999877Syar goto nextline; 780102183Syar hrp->hostname = NULL; 781102183Syar hrp->hostinfo = NULL; 782100182Syar insert = 1; 783100182Syar } else 784100182Syar insert = 0; /* host already in the chain */ 785102183Syar if (hrp->hostinfo) 786102183Syar freeaddrinfo(hrp->hostinfo); 78757124Sshin hrp->hostinfo = res; 78857124Sshin 78925283Sdavidn /* 79025283Sdavidn * determine hostname to use. 79156668Sshin * force defined name if there is a valid alias 79225283Sdavidn * otherwise fallback to primary hostname 79325283Sdavidn */ 79456668Sshin /* XXX: getaddrinfo() can't do alias check */ 79557124Sshin switch(hrp->hostinfo->ai_family) { 79656668Sshin case AF_INET: 797100259Syar addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; 798100259Syar addrsize = sizeof(struct in_addr); 79956668Sshin break; 80056668Sshin case AF_INET6: 801100259Syar addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; 802100259Syar addrsize = sizeof(struct in6_addr); 80356668Sshin break; 80456668Sshin default: 80556668Sshin /* should not reach here */ 80657124Sshin if (hrp->hostinfo != NULL) 80757124Sshin freeaddrinfo(hrp->hostinfo); 80856668Sshin free(hrp); 80999877Syar goto nextline; 81056668Sshin /* NOTREACHED */ 81156668Sshin } 812100612Syar if ((hp = getipnodebyaddr(addr, addrsize, 81357124Sshin hrp->hostinfo->ai_family, 81456668Sshin &hp_error)) != NULL) { 815100182Syar if (strcmp(vhost, hp->h_name) != 0) { 81625283Sdavidn if (hp->h_aliases == NULL) 817100182Syar vhost = hp->h_name; 81825283Sdavidn else { 81925283Sdavidn i = 0; 82025283Sdavidn while (hp->h_aliases[i] && 821100182Syar strcmp(vhost, hp->h_aliases[i]) != 0) 82225283Sdavidn ++i; 82325283Sdavidn if (hp->h_aliases[i] == NULL) 824100182Syar vhost = hp->h_name; 82525283Sdavidn } 82625283Sdavidn } 82725283Sdavidn } 828102183Syar if (hrp->hostname && 829102183Syar strcmp(hrp->hostname, vhost) != 0) { 830102183Syar free(hrp->hostname); 831102183Syar hrp->hostname = NULL; 832102183Syar } 833102183Syar if (hrp->hostname == NULL && 834102183Syar (hrp->hostname = strdup(vhost)) == NULL) 835100182Syar goto nextline; 836100182Syar hrp->anonuser = anonuser; 837100182Syar hrp->statfile = statfile; 838100182Syar hrp->welcome = welcome; 839100182Syar hrp->loginmsg = loginmsg; 840100182Syar if (insert) { 841100182Syar hrp->next = NULL; 842100182Syar lhrp->next = hrp; 843100182Syar lhrp = hrp; 844100182Syar } 845100263Syar if (hp) 846100263Syar freehostent(hp); 84756668Sshin } 84899877Syarnextline: 84999877Syar if (mp) 85099877Syar free(mp); 85125283Sdavidn } 85225283Sdavidn (void) fclose(fp); 85325283Sdavidn } 85425283Sdavidn} 85525283Sdavidn 85625283Sdavidnstatic void 85790148Simpselecthost(union sockunion *su) 85825283Sdavidn{ 85925283Sdavidn struct ftphost *hrp; 86056668Sshin u_int16_t port; 86156668Sshin#ifdef INET6 86256668Sshin struct in6_addr *mapped_in6 = NULL; 86356668Sshin#endif 86457124Sshin struct addrinfo *hi; 86525283Sdavidn 86656668Sshin#ifdef INET6 86756668Sshin /* 86856668Sshin * XXX IPv4 mapped IPv6 addr consideraton, 86956668Sshin * specified in rfc2373. 87056668Sshin */ 87156668Sshin if (su->su_family == AF_INET6 && 87256668Sshin IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) 87356668Sshin mapped_in6 = &su->su_sin6.sin6_addr; 87456668Sshin#endif 87556668Sshin 87625283Sdavidn hrp = thishost = firsthost; /* default */ 87756668Sshin port = su->su_port; 87856668Sshin su->su_port = 0; 87925283Sdavidn while (hrp != NULL) { 88062100Sdavidn for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { 88157124Sshin if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { 88225283Sdavidn thishost = hrp; 88325283Sdavidn break; 88425283Sdavidn } 88556668Sshin#ifdef INET6 88656668Sshin /* XXX IPv4 mapped IPv6 addr consideraton */ 88757124Sshin if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && 88856668Sshin (memcmp(&mapped_in6->s6_addr[12], 88957124Sshin &((struct sockaddr_in *)hi->ai_addr)->sin_addr, 89056668Sshin sizeof(struct in_addr)) == 0)) { 89156668Sshin thishost = hrp; 89256668Sshin break; 89356668Sshin } 89456668Sshin#endif 89562100Sdavidn } 89662100Sdavidn hrp = hrp->next; 89725283Sdavidn } 89856668Sshin su->su_port = port; 89925283Sdavidn /* setup static variables as appropriate */ 90025283Sdavidn hostname = thishost->hostname; 90125283Sdavidn ftpuser = thishost->anonuser; 90225283Sdavidn} 90325283Sdavidn#endif 90425283Sdavidn 90525283Sdavidn/* 9061592Srgrimes * Helper function for sgetpwnam(). 9071592Srgrimes */ 9081592Srgrimesstatic char * 90990148Simpsgetsave(char *s) 9101592Srgrimes{ 9111592Srgrimes char *new = malloc((unsigned) strlen(s) + 1); 9121592Srgrimes 9131592Srgrimes if (new == NULL) { 9141592Srgrimes perror_reply(421, "Local resource failure: malloc"); 9151592Srgrimes dologout(1); 9161592Srgrimes /* NOTREACHED */ 9171592Srgrimes } 9181592Srgrimes (void) strcpy(new, s); 9191592Srgrimes return (new); 9201592Srgrimes} 9211592Srgrimes 9221592Srgrimes/* 9231592Srgrimes * Save the result of a getpwnam. Used for USER command, since 9241592Srgrimes * the data returned must not be clobbered by any other command 9251592Srgrimes * (e.g., globbing). 9261592Srgrimes */ 9271592Srgrimesstatic struct passwd * 92890148Simpsgetpwnam(char *name) 9291592Srgrimes{ 9301592Srgrimes static struct passwd save; 9311592Srgrimes struct passwd *p; 9321592Srgrimes 9331592Srgrimes if ((p = getpwnam(name)) == NULL) 9341592Srgrimes return (p); 9351592Srgrimes if (save.pw_name) { 9361592Srgrimes free(save.pw_name); 9371592Srgrimes free(save.pw_passwd); 9381592Srgrimes free(save.pw_gecos); 9391592Srgrimes free(save.pw_dir); 9401592Srgrimes free(save.pw_shell); 9411592Srgrimes } 9421592Srgrimes save = *p; 9431592Srgrimes save.pw_name = sgetsave(p->pw_name); 9441592Srgrimes save.pw_passwd = sgetsave(p->pw_passwd); 9451592Srgrimes save.pw_gecos = sgetsave(p->pw_gecos); 9461592Srgrimes save.pw_dir = sgetsave(p->pw_dir); 9471592Srgrimes save.pw_shell = sgetsave(p->pw_shell); 9481592Srgrimes return (&save); 9491592Srgrimes} 9501592Srgrimes 9511592Srgrimesstatic int login_attempts; /* number of failed login attempts */ 9521592Srgrimesstatic int askpasswd; /* had user command, ask for passwd */ 95364778Ssheldonhstatic char curname[MAXLOGNAME]; /* current USER name */ 9541592Srgrimes 9551592Srgrimes/* 9561592Srgrimes * USER command. 9571592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable; 9581592Srgrimes * sets askpasswd if a PASS command is expected. If logged in previously, 9591592Srgrimes * need to reset state. If name is "ftp" or "anonymous", the name is not in 9601592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 9611592Srgrimes * If account doesn't exist, ask for passwd anyway. Otherwise, check user 9621592Srgrimes * requesting login privileges. Disallow anyone who does not have a standard 9631592Srgrimes * shell as returned by getusershell(). Disallow anyone mentioned in the file 9641592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 9651592Srgrimes */ 9661592Srgrimesvoid 96790148Simpuser(char *name) 9681592Srgrimes{ 9691592Srgrimes char *cp, *shell; 9701592Srgrimes 9711592Srgrimes if (logged_in) { 9721592Srgrimes if (guest) { 9731592Srgrimes reply(530, "Can't change user from guest login."); 9741592Srgrimes return; 97517435Spst } else if (dochroot) { 97617435Spst reply(530, "Can't change user from chroot user."); 97717435Spst return; 9781592Srgrimes } 9791592Srgrimes end_login(); 9801592Srgrimes } 9811592Srgrimes 9821592Srgrimes guest = 0; 9831592Srgrimes if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 98436349Ssteve if (checkuser(_PATH_FTPUSERS, "ftp", 0) || 98536349Ssteve checkuser(_PATH_FTPUSERS, "anonymous", 0)) 9861592Srgrimes reply(530, "User %s access denied.", name); 98725283Sdavidn#ifdef VIRTUAL_HOSTING 98825283Sdavidn else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { 98925283Sdavidn#else 9901592Srgrimes else if ((pw = sgetpwnam("ftp")) != NULL) { 99125283Sdavidn#endif 9921592Srgrimes guest = 1; 9931592Srgrimes askpasswd = 1; 9941592Srgrimes reply(331, 9953938Spst "Guest login ok, send your email address as password."); 9961592Srgrimes } else 9971592Srgrimes reply(530, "User %s unknown.", name); 9981592Srgrimes if (!askpasswd && logging) 9991592Srgrimes syslog(LOG_NOTICE, 10001592Srgrimes "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 10011592Srgrimes return; 10021592Srgrimes } 100320042Storstenb if (anon_only != 0) { 100420042Storstenb reply(530, "Sorry, only anonymous ftp allowed."); 100520042Storstenb return; 100620042Storstenb } 100720042Storstenb 100817478Smarkm if ((pw = sgetpwnam(name))) { 10091592Srgrimes if ((shell = pw->pw_shell) == NULL || *shell == 0) 10101592Srgrimes shell = _PATH_BSHELL; 10111592Srgrimes while ((cp = getusershell()) != NULL) 10121592Srgrimes if (strcmp(cp, shell) == 0) 10131592Srgrimes break; 10141592Srgrimes endusershell(); 10151592Srgrimes 101636349Ssteve if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) { 10171592Srgrimes reply(530, "User %s access denied.", name); 10181592Srgrimes if (logging) 10191592Srgrimes syslog(LOG_NOTICE, 10201592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 10211592Srgrimes remotehost, name); 10221592Srgrimes pw = (struct passwd *) NULL; 10231592Srgrimes return; 10241592Srgrimes } 10251592Srgrimes } 10261592Srgrimes if (logging) 10271592Srgrimes strncpy(curname, name, sizeof(curname)-1); 102888763Sache 102988763Sache pwok = 0; 103079469Smarkm#ifdef USE_PAM 103179469Smarkm /* XXX Kluge! The conversation mechanism needs to be fixed. */ 103288763Sache#endif 103388763Sache if (opiechallenge(&opiedata, name, opieprompt) == 0) { 103488763Sache pwok = (pw != NULL) && 103588763Sache opieaccessfile(remotehost) && 103688763Sache opiealways(pw->pw_dir); 103788763Sache reply(331, "Response to %s %s for %s.", 103888763Sache opieprompt, pwok ? "requested" : "required", name); 103988763Sache } else { 104088763Sache pwok = 1; 104184146Sache reply(331, "Password required for %s.", name); 104288763Sache } 10431592Srgrimes askpasswd = 1; 10441592Srgrimes /* 10451592Srgrimes * Delay before reading passwd after first failed 10461592Srgrimes * attempt to slow down passwd-guessing programs. 10471592Srgrimes */ 10481592Srgrimes if (login_attempts) 10491592Srgrimes sleep((unsigned) login_attempts); 10501592Srgrimes} 10511592Srgrimes 10521592Srgrimes/* 105317435Spst * Check if a user is in the file "fname" 10541592Srgrimes */ 10551592Srgrimesstatic int 105690148Simpcheckuser(char *fname, char *name, int pwset) 10571592Srgrimes{ 10581592Srgrimes FILE *fd; 10591592Srgrimes int found = 0; 106099877Syar size_t len; 106199877Syar char *line, *mp, *p; 10621592Srgrimes 106317435Spst if ((fd = fopen(fname, "r")) != NULL) { 106499877Syar while (!found && (line = fgetln(fd, &len)) != NULL) { 106599877Syar /* skip comments */ 106699877Syar if (line[0] == '#') 106799877Syar continue; 106899877Syar if (line[len - 1] == '\n') { 106999877Syar line[len - 1] = '\0'; 107099877Syar mp = NULL; 107199877Syar } else { 107299877Syar if ((mp = malloc(len + 1)) == NULL) 107399877Syar fatalerror("Ran out of memory."); 107499877Syar memcpy(mp, line, len); 107599877Syar mp[len] = '\0'; 107699877Syar line = mp; 107799877Syar } 107899877Syar /* avoid possible leading and trailing whitespace */ 107999877Syar p = strtok(line, " \t"); 108099877Syar /* skip empty lines */ 108199877Syar if (p == NULL) 108299877Syar goto nextline; 108399877Syar /* 108499877Syar * if first chr is '@', check group membership 108599877Syar */ 108699877Syar if (p[0] == '@') { 108799877Syar int i = 0; 108899877Syar struct group *grp; 108999877Syar 109099877Syar if ((grp = getgrnam(p+1)) == NULL) 109199877Syar goto nextline; 109225187Sdavidn /* 109399877Syar * Check user's default group 109425187Sdavidn */ 109599877Syar if (pwset && grp->gr_gid == pw->pw_gid) 109699877Syar found = 1; 109725187Sdavidn /* 109899877Syar * Check supplementary groups 109925187Sdavidn */ 110099877Syar while (!found && grp->gr_mem[i]) 110199877Syar found = strcmp(name, 110299877Syar grp->gr_mem[i++]) 110399877Syar == 0; 11041592Srgrimes } 110599877Syar /* 110699877Syar * Otherwise, just check for username match 110799877Syar */ 110899877Syar else 110999877Syar found = strcmp(p, name) == 0; 111099877Syarnextline: 111199877Syar if (mp) 111299877Syar free(mp); 111399877Syar } 11141592Srgrimes (void) fclose(fd); 11151592Srgrimes } 11161592Srgrimes return (found); 11171592Srgrimes} 11181592Srgrimes 11191592Srgrimes/* 11201592Srgrimes * Terminate login as previous user, if any, resetting state; 11211592Srgrimes * used when USER command is given or login fails. 11221592Srgrimes */ 11231592Srgrimesstatic void 112490148Simpend_login(void) 11251592Srgrimes{ 112674874Smarkm#ifdef USE_PAM 112774874Smarkm int e; 112874874Smarkm#endif 11291592Srgrimes 11301592Srgrimes (void) seteuid((uid_t)0); 11311592Srgrimes if (logged_in) 113289920Sume ftpd_logwtmp(ttyline, "", NULL); 11331592Srgrimes pw = NULL; 113425101Sdavidn#ifdef LOGIN_CAP 113525101Sdavidn setusercontext(NULL, getpwuid(0), (uid_t)0, 113625101Sdavidn LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); 113725101Sdavidn#endif 113874874Smarkm#ifdef USE_PAM 113974874Smarkm if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 114074874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 114174874Smarkm if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 114274874Smarkm syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 114374874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 114474874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 114574874Smarkm pamh = NULL; 114674874Smarkm#endif 11471592Srgrimes logged_in = 0; 11481592Srgrimes guest = 0; 114917435Spst dochroot = 0; 11501592Srgrimes} 11511592Srgrimes 115274874Smarkm#ifdef USE_PAM 115351433Smarkm 115451433Smarkm/* 115551433Smarkm * the following code is stolen from imap-uw PAM authentication module and 115651433Smarkm * login.c 115751433Smarkm */ 115851433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL) 115951433Smarkm 116051433Smarkmstruct cred_t { 116151433Smarkm const char *uname; /* user name */ 116251433Smarkm const char *pass; /* password */ 116351433Smarkm}; 116451433Smarkmtypedef struct cred_t cred_t; 116551433Smarkm 116651433Smarkmstatic int 116751433Smarkmauth_conv(int num_msg, const struct pam_message **msg, 116851433Smarkm struct pam_response **resp, void *appdata) 116951433Smarkm{ 117051433Smarkm int i; 117151433Smarkm cred_t *cred = (cred_t *) appdata; 117291244Sdes struct pam_response *reply; 117351433Smarkm 117491244Sdes reply = calloc(num_msg, sizeof *reply); 117591244Sdes if (reply == NULL) 117691244Sdes return PAM_BUF_ERR; 117791244Sdes 117851433Smarkm for (i = 0; i < num_msg; i++) { 117951433Smarkm switch (msg[i]->msg_style) { 118051433Smarkm case PAM_PROMPT_ECHO_ON: /* assume want user name */ 118151433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 118251433Smarkm reply[i].resp = COPY_STRING(cred->uname); 118351433Smarkm /* PAM frees resp. */ 118451433Smarkm break; 118551433Smarkm case PAM_PROMPT_ECHO_OFF: /* assume want password */ 118651433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 118751433Smarkm reply[i].resp = COPY_STRING(cred->pass); 118851433Smarkm /* PAM frees resp. */ 118951433Smarkm break; 119051433Smarkm case PAM_TEXT_INFO: 119151433Smarkm case PAM_ERROR_MSG: 119251433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 119351433Smarkm reply[i].resp = NULL; 119451433Smarkm break; 119551433Smarkm default: /* unknown message style */ 119651433Smarkm free(reply); 119751433Smarkm return PAM_CONV_ERR; 119851433Smarkm } 119951433Smarkm } 120051433Smarkm 120151433Smarkm *resp = reply; 120251433Smarkm return PAM_SUCCESS; 120351433Smarkm} 120451433Smarkm 120551433Smarkm/* 120651433Smarkm * Attempt to authenticate the user using PAM. Returns 0 if the user is 120751433Smarkm * authenticated, or 1 if not authenticated. If some sort of PAM system 120851433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 120951433Smarkm * function returns -1. This can be used as an indication that we should 121051433Smarkm * fall back to a different authentication mechanism. 121151433Smarkm */ 121251433Smarkmstatic int 121351433Smarkmauth_pam(struct passwd **ppw, const char *pass) 121451433Smarkm{ 121551433Smarkm pam_handle_t *pamh = NULL; 121651433Smarkm const char *tmpl_user; 121751433Smarkm const void *item; 121851433Smarkm int rval; 121951433Smarkm int e; 122051433Smarkm cred_t auth_cred = { (*ppw)->pw_name, pass }; 122151433Smarkm struct pam_conv conv = { &auth_conv, &auth_cred }; 122251433Smarkm 122351433Smarkm e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 122451433Smarkm if (e != PAM_SUCCESS) { 122551433Smarkm syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); 122651433Smarkm return -1; 122751433Smarkm } 122851433Smarkm 122967007Sguido e = pam_set_item(pamh, PAM_RHOST, remotehost); 123067007Sguido if (e != PAM_SUCCESS) { 123167007Sguido syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 123267007Sguido pam_strerror(pamh, e)); 123367007Sguido return -1; 123467007Sguido } 123567007Sguido 123651433Smarkm e = pam_authenticate(pamh, 0); 123751433Smarkm switch (e) { 123851433Smarkm case PAM_SUCCESS: 123951433Smarkm /* 124051433Smarkm * With PAM we support the concept of a "template" 124151433Smarkm * user. The user enters a login name which is 124251433Smarkm * authenticated by PAM, usually via a remote service 124351433Smarkm * such as RADIUS or TACACS+. If authentication 124451433Smarkm * succeeds, a different but related "template" name 124551433Smarkm * is used for setting the credentials, shell, and 124651433Smarkm * home directory. The name the user enters need only 124751433Smarkm * exist on the remote authentication server, but the 124851433Smarkm * template name must be present in the local password 124951433Smarkm * database. 125051433Smarkm * 125151433Smarkm * This is supported by two various mechanisms in the 125251433Smarkm * individual modules. However, from the application's 125351433Smarkm * point of view, the template user is always passed 125451433Smarkm * back as a changed value of the PAM_USER item. 125551433Smarkm */ 125651433Smarkm if ((e = pam_get_item(pamh, PAM_USER, &item)) == 125751433Smarkm PAM_SUCCESS) { 125851433Smarkm tmpl_user = (const char *) item; 125951433Smarkm if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 126051433Smarkm *ppw = getpwnam(tmpl_user); 126151433Smarkm } else 126251433Smarkm syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 126351433Smarkm pam_strerror(pamh, e)); 126451433Smarkm rval = 0; 126551433Smarkm break; 126651433Smarkm 126751433Smarkm case PAM_AUTH_ERR: 126851433Smarkm case PAM_USER_UNKNOWN: 126951433Smarkm case PAM_MAXTRIES: 127051433Smarkm rval = 1; 127151433Smarkm break; 127251433Smarkm 127351433Smarkm default: 127474874Smarkm syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 127551433Smarkm rval = -1; 127651433Smarkm break; 127751433Smarkm } 127851433Smarkm 127974874Smarkm if (rval == 0) { 128074874Smarkm e = pam_acct_mgmt(pamh, 0); 128174874Smarkm if (e == PAM_NEW_AUTHTOK_REQD) { 128274874Smarkm e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 128374874Smarkm if (e != PAM_SUCCESS) { 128474874Smarkm syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e)); 128574874Smarkm rval = 1; 128674874Smarkm } 128774874Smarkm } else if (e != PAM_SUCCESS) { 128874874Smarkm rval = 1; 128974874Smarkm } 129051433Smarkm } 129174874Smarkm 129274874Smarkm if (rval != 0) { 129374874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 129474874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 129574874Smarkm } 129674874Smarkm pamh = NULL; 129774874Smarkm } 129851433Smarkm return rval; 129951433Smarkm} 130051433Smarkm 130174874Smarkm#endif /* USE_PAM */ 130251433Smarkm 13031592Srgrimesvoid 130490148Simppass(char *passwd) 13051592Srgrimes{ 130617435Spst int rval; 13071592Srgrimes FILE *fd; 130825101Sdavidn#ifdef LOGIN_CAP 130925101Sdavidn login_cap_t *lc = NULL; 131025101Sdavidn#endif 131174874Smarkm#ifdef USE_PAM 131274874Smarkm int e; 131374874Smarkm#endif 131488763Sache char *xpasswd; 13151592Srgrimes 13161592Srgrimes if (logged_in || askpasswd == 0) { 13171592Srgrimes reply(503, "Login with USER first."); 13181592Srgrimes return; 13191592Srgrimes } 13201592Srgrimes askpasswd = 0; 13211592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 132217435Spst if (pw == NULL) { 132317435Spst rval = 1; /* failure below */ 132417435Spst goto skip; 132517435Spst } 132674874Smarkm#ifdef USE_PAM 132751433Smarkm rval = auth_pam(&pw, passwd); 132889622Sache if (rval >= 0) { 132989622Sache opieunlock(); 133017435Spst goto skip; 133189622Sache } 133289622Sache#endif 133388763Sache if (opieverify(&opiedata, passwd) == 0) 133488763Sache xpasswd = pw->pw_passwd; 133589622Sache else if (pwok) { 133688763Sache xpasswd = crypt(passwd, pw->pw_passwd); 133789622Sache if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 133889622Sache xpasswd = ":"; 133989622Sache } else { 134088763Sache rval = 1; 134188763Sache goto skip; 134288763Sache } 134388763Sache rval = strcmp(pw->pw_passwd, xpasswd); 134489622Sache if (pw->pw_expire && time(NULL) >= pw->pw_expire) 134517435Spst rval = 1; /* failure */ 134617435Spstskip: 134717435Spst /* 134817435Spst * If rval == 1, the user failed the authentication check 134951433Smarkm * above. If rval == 0, either PAM or local authentication 135017435Spst * succeeded. 135117435Spst */ 135217435Spst if (rval) { 13531592Srgrimes reply(530, "Login incorrect."); 13541592Srgrimes if (logging) 13551592Srgrimes syslog(LOG_NOTICE, 13561592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 13571592Srgrimes remotehost, curname); 13581592Srgrimes pw = NULL; 13591592Srgrimes if (login_attempts++ >= 5) { 13601592Srgrimes syslog(LOG_NOTICE, 13611592Srgrimes "repeated login failures from %s", 13621592Srgrimes remotehost); 13631592Srgrimes exit(0); 13641592Srgrimes } 13651592Srgrimes return; 13661592Srgrimes } 13671592Srgrimes } 13681592Srgrimes login_attempts = 0; /* this time successful */ 13691592Srgrimes if (setegid((gid_t)pw->pw_gid) < 0) { 13701592Srgrimes reply(550, "Can't set gid."); 13711592Srgrimes return; 13721592Srgrimes } 137325101Sdavidn /* May be overridden by login.conf */ 137425101Sdavidn (void) umask(defumask); 137525101Sdavidn#ifdef LOGIN_CAP 137625674Sdavidn if ((lc = login_getpwclass(pw)) != NULL) { 137725101Sdavidn char remote_ip[MAXHOSTNAMELEN]; 137825101Sdavidn 137956668Sshin getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 138056668Sshin remote_ip, sizeof(remote_ip) - 1, NULL, 0, 138199255Sume NI_NUMERICHOST); 138225101Sdavidn remote_ip[sizeof(remote_ip) - 1] = 0; 138325101Sdavidn if (!auth_hostok(lc, remotehost, remote_ip)) { 138425101Sdavidn syslog(LOG_INFO|LOG_AUTH, 138525101Sdavidn "FTP LOGIN FAILED (HOST) as %s: permission denied.", 138625101Sdavidn pw->pw_name); 138725101Sdavidn reply(530, "Permission denied.\n"); 138825101Sdavidn pw = NULL; 138925101Sdavidn return; 139025101Sdavidn } 139125101Sdavidn if (!auth_timeok(lc, time(NULL))) { 139225101Sdavidn reply(530, "Login not available right now.\n"); 139325101Sdavidn pw = NULL; 139425101Sdavidn return; 139525101Sdavidn } 139625101Sdavidn } 139725101Sdavidn setusercontext(lc, pw, (uid_t)0, 139840310Sdes LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| 139940310Sdes LOGIN_SETRESOURCES|LOGIN_SETUMASK); 140025101Sdavidn#else 140140310Sdes setlogin(pw->pw_name); 14021592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 140325101Sdavidn#endif 14041592Srgrimes 140574874Smarkm#ifdef USE_PAM 140674874Smarkm if (pamh) { 140774874Smarkm if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 140874874Smarkm syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 140974874Smarkm } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 141074874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 141174874Smarkm } 141274874Smarkm } 141374874Smarkm#endif 141474874Smarkm 14151592Srgrimes /* open wtmp before chroot */ 141689920Sume ftpd_logwtmp(ttyline, pw->pw_name, (struct sockaddr *)&his_addr); 14171592Srgrimes logged_in = 1; 14181592Srgrimes 141917435Spst if (guest && stats && statfd < 0) 142025283Sdavidn#ifdef VIRTUAL_HOSTING 142125283Sdavidn if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) 142225283Sdavidn#else 14236740Sguido if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) 142425283Sdavidn#endif 14256740Sguido stats = 0; 14266740Sguido 142725101Sdavidn dochroot = 142825101Sdavidn#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 142925101Sdavidn login_getcapbool(lc, "ftp-chroot", 0) || 143025101Sdavidn#endif 143136349Ssteve checkuser(_PATH_FTPCHROOT, pw->pw_name, 1); 14321592Srgrimes if (guest) { 14331592Srgrimes /* 14341592Srgrimes * We MUST do a chdir() after the chroot. Otherwise 14351592Srgrimes * the old current directory will be accessible as "." 14361592Srgrimes * outside the new root! 14371592Srgrimes */ 14381592Srgrimes if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 14391592Srgrimes reply(550, "Can't set guest privileges."); 14401592Srgrimes goto bad; 14411592Srgrimes } 144217435Spst } else if (dochroot) { 144317435Spst if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 144417435Spst reply(550, "Can't change root."); 144517435Spst goto bad; 144617435Spst } 14471592Srgrimes } else if (chdir(pw->pw_dir) < 0) { 14481592Srgrimes if (chdir("/") < 0) { 14491592Srgrimes reply(530, "User %s: can't change directory to %s.", 14501592Srgrimes pw->pw_name, pw->pw_dir); 14511592Srgrimes goto bad; 14521592Srgrimes } else 14531592Srgrimes lreply(230, "No directory! Logging in with home=/"); 14541592Srgrimes } 14551592Srgrimes if (seteuid((uid_t)pw->pw_uid) < 0) { 14561592Srgrimes reply(550, "Can't set uid."); 14571592Srgrimes goto bad; 14581592Srgrimes } 14598696Sdg 14601592Srgrimes /* 14611592Srgrimes * Display a login message, if it exists. 14621592Srgrimes * N.B. reply(230,) must follow the message. 14631592Srgrimes */ 146425283Sdavidn#ifdef VIRTUAL_HOSTING 146525283Sdavidn if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { 146625283Sdavidn#else 14671592Srgrimes if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 146825283Sdavidn#endif 14691592Srgrimes char *cp, line[LINE_MAX]; 14701592Srgrimes 14711592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 14721592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 14731592Srgrimes *cp = '\0'; 14741592Srgrimes lreply(230, "%s", line); 14751592Srgrimes } 14761592Srgrimes (void) fflush(stdout); 14771592Srgrimes (void) fclose(fd); 14781592Srgrimes } 14791592Srgrimes if (guest) { 14806740Sguido if (ident != NULL) 14816740Sguido free(ident); 148217433Spst ident = strdup(passwd); 148317433Spst if (ident == NULL) 148476096Smarkm fatalerror("Ran out of memory."); 148517433Spst 14861592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 14871592Srgrimes#ifdef SETPROCTITLE 148825283Sdavidn#ifdef VIRTUAL_HOSTING 148925283Sdavidn if (thishost != firsthost) 149025283Sdavidn snprintf(proctitle, sizeof(proctitle), 149183308Smikeh "%s: anonymous(%s)/%s", remotehost, hostname, 149283308Smikeh passwd); 149325283Sdavidn else 149425283Sdavidn#endif 149525283Sdavidn snprintf(proctitle, sizeof(proctitle), 149683308Smikeh "%s: anonymous/%s", remotehost, passwd); 149713139Speter setproctitle("%s", proctitle); 14981592Srgrimes#endif /* SETPROCTITLE */ 14991592Srgrimes if (logging) 15001592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 15011592Srgrimes remotehost, passwd); 15021592Srgrimes } else { 150384841Syar if (dochroot) 150484841Syar reply(230, "User %s logged in, " 150584841Syar "access restrictions apply.", pw->pw_name); 150684841Syar else 150784841Syar reply(230, "User %s logged in.", pw->pw_name); 150825986Sdanny 15091592Srgrimes#ifdef SETPROCTITLE 15101592Srgrimes snprintf(proctitle, sizeof(proctitle), 151184842Syar "%s: user/%s", remotehost, pw->pw_name); 151213139Speter setproctitle("%s", proctitle); 15131592Srgrimes#endif /* SETPROCTITLE */ 15141592Srgrimes if (logging) 15151592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 15161592Srgrimes remotehost, pw->pw_name); 15171592Srgrimes } 151825101Sdavidn#ifdef LOGIN_CAP 151925101Sdavidn login_close(lc); 152025101Sdavidn#endif 15211592Srgrimes return; 15221592Srgrimesbad: 15231592Srgrimes /* Forget all about it... */ 152425101Sdavidn#ifdef LOGIN_CAP 152525101Sdavidn login_close(lc); 152625101Sdavidn#endif 15271592Srgrimes end_login(); 15281592Srgrimes} 15291592Srgrimes 15301592Srgrimesvoid 153190148Simpretrieve(char *cmd, char *name) 15321592Srgrimes{ 15331592Srgrimes FILE *fin, *dout; 15341592Srgrimes struct stat st; 153590148Simp int (*closefunc)(FILE *); 153636612Sjb time_t start; 15371592Srgrimes 15381592Srgrimes if (cmd == 0) { 15391592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 15401592Srgrimes st.st_size = 0; 15411592Srgrimes } else { 15421592Srgrimes char line[BUFSIZ]; 15431592Srgrimes 154431973Simp (void) snprintf(line, sizeof(line), cmd, name), name = line; 15451592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 15461592Srgrimes st.st_size = -1; 15471592Srgrimes st.st_blksize = BUFSIZ; 15481592Srgrimes } 15491592Srgrimes if (fin == NULL) { 15501592Srgrimes if (errno != 0) { 15511592Srgrimes perror_reply(550, name); 15521592Srgrimes if (cmd == 0) { 15531592Srgrimes LOGCMD("get", name); 15541592Srgrimes } 15551592Srgrimes } 15561592Srgrimes return; 15571592Srgrimes } 15581592Srgrimes byte_count = -1; 15591592Srgrimes if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { 15601592Srgrimes reply(550, "%s: not a plain file.", name); 15611592Srgrimes goto done; 15621592Srgrimes } 15631592Srgrimes if (restart_point) { 15641592Srgrimes if (type == TYPE_A) { 15651592Srgrimes off_t i, n; 15661592Srgrimes int c; 15671592Srgrimes 15681592Srgrimes n = restart_point; 15691592Srgrimes i = 0; 15701592Srgrimes while (i++ < n) { 15711592Srgrimes if ((c=getc(fin)) == EOF) { 15721592Srgrimes perror_reply(550, name); 15731592Srgrimes goto done; 15741592Srgrimes } 15751592Srgrimes if (c == '\n') 15761592Srgrimes i++; 15771592Srgrimes } 15781592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 15791592Srgrimes perror_reply(550, name); 15801592Srgrimes goto done; 15811592Srgrimes } 15821592Srgrimes } 15831592Srgrimes dout = dataconn(name, st.st_size, "w"); 15841592Srgrimes if (dout == NULL) 15851592Srgrimes goto done; 15866740Sguido time(&start); 15878240Swollman send_data(fin, dout, st.st_blksize, st.st_size, 15888240Swollman restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 15896740Sguido if (cmd == 0 && guest && stats) 159017433Spst logxfer(name, st.st_size, start); 15911592Srgrimes (void) fclose(dout); 15921592Srgrimes data = -1; 15931592Srgrimes pdata = -1; 15941592Srgrimesdone: 15951592Srgrimes if (cmd == 0) 15961592Srgrimes LOGBYTES("get", name, byte_count); 15971592Srgrimes (*closefunc)(fin); 15981592Srgrimes} 15991592Srgrimes 16001592Srgrimesvoid 160190148Simpstore(char *name, char *mode, int unique) 16021592Srgrimes{ 1603101537Syar int fd; 16041592Srgrimes FILE *fout, *din; 16051592Srgrimes struct stat st; 160690148Simp int (*closefunc)(FILE *); 16071592Srgrimes 1608101537Syar if (*mode == 'a') { /* APPE */ 1609101537Syar if (unique) { 1610101537Syar /* Programming error */ 1611101537Syar syslog(LOG_ERR, "Internal: unique flag to APPE"); 1612101537Syar unique = 0; 1613101537Syar } 1614101537Syar if (guest && noguestmod) { 1615101537Syar reply(550, "Appending to existing file denied"); 1616101537Syar goto err; 1617101537Syar } 1618101537Syar restart_point = 0; /* not affected by preceding REST */ 16191592Srgrimes } 1620101537Syar if (unique) /* STOU overrides REST */ 1621101537Syar restart_point = 0; 1622101537Syar if (guest && noguestmod) { 1623101537Syar if (restart_point) { /* guest STOR w/REST */ 1624101537Syar reply(550, "Modifying existing file denied"); 1625101537Syar goto err; 1626101537Syar } else /* treat guest STOR as STOU */ 1627101537Syar unique = 1; 1628101537Syar } 16291592Srgrimes 16301592Srgrimes if (restart_point) 1631101537Syar mode = "r+"; /* so ASCII manual seek can work */ 1632101537Syar if (unique) { 1633101537Syar if ((fd = guniquefd(name, &name)) < 0) 1634101537Syar goto err; 1635101537Syar fout = fdopen(fd, mode); 1636101537Syar } else 1637101537Syar fout = fopen(name, mode); 16381592Srgrimes closefunc = fclose; 16391592Srgrimes if (fout == NULL) { 16401592Srgrimes perror_reply(553, name); 1641101537Syar goto err; 16421592Srgrimes } 16431592Srgrimes byte_count = -1; 16441592Srgrimes if (restart_point) { 16451592Srgrimes if (type == TYPE_A) { 16461592Srgrimes off_t i, n; 16471592Srgrimes int c; 16481592Srgrimes 16491592Srgrimes n = restart_point; 16501592Srgrimes i = 0; 16511592Srgrimes while (i++ < n) { 16521592Srgrimes if ((c=getc(fout)) == EOF) { 16531592Srgrimes perror_reply(550, name); 16541592Srgrimes goto done; 16551592Srgrimes } 16561592Srgrimes if (c == '\n') 16571592Srgrimes i++; 16581592Srgrimes } 16591592Srgrimes /* 16601592Srgrimes * We must do this seek to "current" position 16611592Srgrimes * because we are changing from reading to 16621592Srgrimes * writing. 16631592Srgrimes */ 166482792Sache if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { 16651592Srgrimes perror_reply(550, name); 16661592Srgrimes goto done; 16671592Srgrimes } 16681592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 16691592Srgrimes perror_reply(550, name); 16701592Srgrimes goto done; 16711592Srgrimes } 16721592Srgrimes } 16731592Srgrimes din = dataconn(name, (off_t)-1, "r"); 16741592Srgrimes if (din == NULL) 16751592Srgrimes goto done; 16761592Srgrimes if (receive_data(din, fout) == 0) { 16771592Srgrimes if (unique) 16781592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 16791592Srgrimes name); 16801592Srgrimes else 16811592Srgrimes reply(226, "Transfer complete."); 16821592Srgrimes } 16831592Srgrimes (void) fclose(din); 16841592Srgrimes data = -1; 16851592Srgrimes pdata = -1; 16861592Srgrimesdone: 16871592Srgrimes LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 16881592Srgrimes (*closefunc)(fout); 1689101537Syar return; 1690101537Syarerr: 1691101537Syar LOGCMD(*mode == 'a' ? "append" : "put" , name); 1692101537Syar return; 16931592Srgrimes} 16941592Srgrimes 16951592Srgrimesstatic FILE * 169690148Simpgetdatasock(char *mode) 16971592Srgrimes{ 16981592Srgrimes int on = 1, s, t, tries; 16991592Srgrimes 17001592Srgrimes if (data >= 0) 17011592Srgrimes return (fdopen(data, mode)); 17021592Srgrimes (void) seteuid((uid_t)0); 170356668Sshin 170456668Sshin s = socket(data_dest.su_family, SOCK_STREAM, 0); 17051592Srgrimes if (s < 0) 17061592Srgrimes goto bad; 1707100612Syar if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1708100609Syar syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 17091592Srgrimes /* anchor socket to avoid multi-homing problems */ 171056668Sshin data_source = ctrl_addr; 171156668Sshin data_source.su_port = htons(20); /* ftp-data port */ 17121592Srgrimes for (tries = 1; ; tries++) { 17131592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 171456668Sshin data_source.su_len) >= 0) 17151592Srgrimes break; 17161592Srgrimes if (errno != EADDRINUSE || tries > 10) 17171592Srgrimes goto bad; 17181592Srgrimes sleep(tries); 17191592Srgrimes } 17201592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 17211592Srgrimes#ifdef IP_TOS 172256668Sshin if (data_source.su_family == AF_INET) 172356668Sshin { 17241592Srgrimes on = IPTOS_THROUGHPUT; 1725100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 1726100609Syar syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 172756668Sshin } 17281592Srgrimes#endif 17298240Swollman#ifdef TCP_NOPUSH 17308240Swollman /* 17318240Swollman * Turn off push flag to keep sender TCP from sending short packets 17328240Swollman * at the boundaries of each write(). Should probably do a SO_SNDBUF 17338240Swollman * to set the send buffer size as well, but that may not be desirable 17348240Swollman * in heavy-load situations. 17358240Swollman */ 17368240Swollman on = 1; 1737100612Syar if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 1738100609Syar syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 17398240Swollman#endif 17408240Swollman#ifdef SO_SNDBUF 17418240Swollman on = 65536; 1742100612Syar if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof on) < 0) 1743100609Syar syslog(LOG_WARNING, "data setsockopt (SO_SNDBUF): %m"); 17448240Swollman#endif 17458240Swollman 17461592Srgrimes return (fdopen(s, mode)); 17471592Srgrimesbad: 17481592Srgrimes /* Return the real value of errno (close may change it) */ 17491592Srgrimes t = errno; 17501592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 17511592Srgrimes (void) close(s); 17521592Srgrimes errno = t; 17531592Srgrimes return (NULL); 17541592Srgrimes} 17551592Srgrimes 17561592Srgrimesstatic FILE * 175790148Simpdataconn(char *name, off_t size, char *mode) 17581592Srgrimes{ 17591592Srgrimes char sizebuf[32]; 17601592Srgrimes FILE *file; 17611592Srgrimes int retry = 0, tos; 17621592Srgrimes 17631592Srgrimes file_size = size; 17641592Srgrimes byte_count = 0; 17651592Srgrimes if (size != (off_t) -1) 176631973Simp (void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size); 17671592Srgrimes else 176831973Simp *sizebuf = '\0'; 17691592Srgrimes if (pdata >= 0) { 177056668Sshin union sockunion from; 177186628Syar int flags; 177256668Sshin int s, fromlen = ctrl_addr.su_len; 177312532Sguido struct timeval timeout; 177412532Sguido fd_set set; 17751592Srgrimes 177612532Sguido FD_ZERO(&set); 177712532Sguido FD_SET(pdata, &set); 177812532Sguido 177912532Sguido timeout.tv_usec = 0; 178012532Sguido timeout.tv_sec = 120; 178112532Sguido 178286628Syar /* 178386628Syar * Granted a socket is in the blocking I/O mode, 178486628Syar * accept() will block after a successful select() 178586628Syar * if the selected connection dies in between. 178686628Syar * Therefore set the non-blocking I/O flag here. 178786628Syar */ 178886628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 178986628Syar fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 179086628Syar goto pdata_err; 179186628Syar if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) <= 0 || 179286628Syar (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 179386628Syar goto pdata_err; 17941592Srgrimes (void) close(pdata); 17951592Srgrimes pdata = s; 179686628Syar /* 1797101809Syar * Unset the inherited non-blocking I/O flag 1798101809Syar * on the child socket so stdio can work on it. 179986628Syar */ 180086628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 180186628Syar fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 180286628Syar goto pdata_err; 18031592Srgrimes#ifdef IP_TOS 180456668Sshin if (from.su_family == AF_INET) 180556668Sshin { 180617435Spst tos = IPTOS_THROUGHPUT; 1807100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 1808100609Syar syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 180956668Sshin } 18101592Srgrimes#endif 18111592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 18121592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 18131592Srgrimes return (fdopen(pdata, mode)); 181486628Syarpdata_err: 181586628Syar reply(425, "Can't open data connection."); 181686628Syar (void) close(pdata); 181786628Syar pdata = -1; 181886628Syar return (NULL); 18191592Srgrimes } 18201592Srgrimes if (data >= 0) { 18211592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 18221592Srgrimes name, sizebuf); 18231592Srgrimes usedefault = 1; 18241592Srgrimes return (fdopen(data, mode)); 18251592Srgrimes } 18261592Srgrimes if (usedefault) 18271592Srgrimes data_dest = his_addr; 18281592Srgrimes usedefault = 1; 18291592Srgrimes file = getdatasock(mode); 18301592Srgrimes if (file == NULL) { 183156668Sshin char hostbuf[BUFSIZ], portbuf[BUFSIZ]; 183256668Sshin getnameinfo((struct sockaddr *)&data_source, 183356668Sshin data_source.su_len, hostbuf, sizeof(hostbuf) - 1, 183456668Sshin portbuf, sizeof(portbuf), 183599255Sume NI_NUMERICHOST|NI_NUMERICSERV); 183656668Sshin reply(425, "Can't create data socket (%s,%s): %s.", 183756668Sshin hostbuf, portbuf, strerror(errno)); 18381592Srgrimes return (NULL); 18391592Srgrimes } 18401592Srgrimes data = fileno(file); 18411592Srgrimes while (connect(data, (struct sockaddr *)&data_dest, 184256668Sshin data_dest.su_len) < 0) { 18431592Srgrimes if (errno == EADDRINUSE && retry < swaitmax) { 18441592Srgrimes sleep((unsigned) swaitint); 18451592Srgrimes retry += swaitint; 18461592Srgrimes continue; 18471592Srgrimes } 18481592Srgrimes perror_reply(425, "Can't build data connection"); 18491592Srgrimes (void) fclose(file); 18501592Srgrimes data = -1; 18511592Srgrimes return (NULL); 18521592Srgrimes } 18531592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 18541592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 18551592Srgrimes return (file); 18561592Srgrimes} 18571592Srgrimes 18581592Srgrimes/* 18591592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate 18608240Swollman * encapsulation of the data subject to Mode, Structure, and Type. 18611592Srgrimes * 18621592Srgrimes * NB: Form isn't handled. 18631592Srgrimes */ 186489935Syarstatic int 186590148Simpsend_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg) 18661592Srgrimes{ 186770205Sdan int c, filefd, netfd; 186870205Sdan char *buf; 186970205Sdan off_t cnt; 18701592Srgrimes 18711592Srgrimes transflag++; 18721592Srgrimes switch (type) { 18731592Srgrimes 18741592Srgrimes case TYPE_A: 18751592Srgrimes while ((c = getc(instr)) != EOF) { 187689935Syar if (recvurg) 187789935Syar goto got_oob; 18781592Srgrimes byte_count++; 18791592Srgrimes if (c == '\n') { 18801592Srgrimes if (ferror(outstr)) 18811592Srgrimes goto data_err; 18821592Srgrimes (void) putc('\r', outstr); 18831592Srgrimes } 18841592Srgrimes (void) putc(c, outstr); 18851592Srgrimes } 188689935Syar if (recvurg) 188789935Syar goto got_oob; 18881592Srgrimes fflush(outstr); 18891592Srgrimes transflag = 0; 18901592Srgrimes if (ferror(instr)) 18911592Srgrimes goto file_err; 18921592Srgrimes if (ferror(outstr)) 18931592Srgrimes goto data_err; 18941592Srgrimes reply(226, "Transfer complete."); 189589935Syar return (0); 18961592Srgrimes 18971592Srgrimes case TYPE_I: 18981592Srgrimes case TYPE_L: 18998240Swollman /* 19008240Swollman * isreg is only set if we are not doing restart and we 19018240Swollman * are sending a regular file 19028240Swollman */ 19038240Swollman netfd = fileno(outstr); 19048870Srgrimes filefd = fileno(instr); 19058240Swollman 190670205Sdan if (isreg) { 190770205Sdan 190870205Sdan off_t offset; 190970205Sdan int err; 191070205Sdan 191170205Sdan err = cnt = offset = 0; 191270205Sdan 191390604Smaxim while (err != -1 && filesize > 0) { 191490604Smaxim err = sendfile(filefd, netfd, offset, 0, 191570205Sdan (struct sf_hdtr *) NULL, &cnt, 0); 191699212Smaxim /* 191799212Smaxim * Calculate byte_count before OOB processing. 191899212Smaxim * It can be used in myoob() later. 191999212Smaxim */ 192099212Smaxim byte_count += cnt; 192189935Syar if (recvurg) 192289935Syar goto got_oob; 192370205Sdan offset += cnt; 192490604Smaxim filesize -= cnt; 19258240Swollman 192670205Sdan if (err == -1) { 192770205Sdan if (!cnt) 192870205Sdan goto oldway; 192970205Sdan 193070205Sdan goto data_err; 193170205Sdan } 193270205Sdan } 193370205Sdan 193499318Sdan transflag = 0; 19358240Swollman reply(226, "Transfer complete."); 193689935Syar return (0); 19378240Swollman } 19388240Swollman 19398240Swollmanoldway: 19401592Srgrimes if ((buf = malloc((u_int)blksize)) == NULL) { 19411592Srgrimes transflag = 0; 19421592Srgrimes perror_reply(451, "Local resource failure: malloc"); 194389935Syar return (-1); 19441592Srgrimes } 19458870Srgrimes 19461592Srgrimes while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 19471592Srgrimes write(netfd, buf, cnt) == cnt) 19481592Srgrimes byte_count += cnt; 19491592Srgrimes transflag = 0; 19501592Srgrimes (void)free(buf); 19511592Srgrimes if (cnt != 0) { 19521592Srgrimes if (cnt < 0) 19531592Srgrimes goto file_err; 19541592Srgrimes goto data_err; 19551592Srgrimes } 19561592Srgrimes reply(226, "Transfer complete."); 195789935Syar return (0); 19581592Srgrimes default: 19591592Srgrimes transflag = 0; 19601592Srgrimes reply(550, "Unimplemented TYPE %d in send_data", type); 196189935Syar return (-1); 19621592Srgrimes } 19631592Srgrimes 19641592Srgrimesdata_err: 19651592Srgrimes transflag = 0; 19661592Srgrimes perror_reply(426, "Data connection"); 196789935Syar return (-1); 19681592Srgrimes 19691592Srgrimesfile_err: 19701592Srgrimes transflag = 0; 19711592Srgrimes perror_reply(551, "Error on input file"); 197289935Syar return (-1); 197389935Syar 197489935Syargot_oob: 197589935Syar myoob(); 197689935Syar recvurg = 0; 197789935Syar transflag = 0; 197889935Syar return (-1); 19791592Srgrimes} 19801592Srgrimes 19811592Srgrimes/* 19821592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 19831592Srgrimes * the data subject to Mode, Structure, and Type. 19841592Srgrimes * 19851592Srgrimes * N.B.: Form isn't handled. 19861592Srgrimes */ 19871592Srgrimesstatic int 198890148Simpreceive_data(FILE *instr, FILE *outstr) 19891592Srgrimes{ 19901592Srgrimes int c; 199117433Spst int cnt, bare_lfs; 19921592Srgrimes char buf[BUFSIZ]; 19931592Srgrimes 19941592Srgrimes transflag++; 199517433Spst bare_lfs = 0; 199617433Spst 19971592Srgrimes switch (type) { 19981592Srgrimes 19991592Srgrimes case TYPE_I: 20001592Srgrimes case TYPE_L: 20011592Srgrimes while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 200289935Syar if (recvurg) 200389935Syar goto got_oob; 20041592Srgrimes if (write(fileno(outstr), buf, cnt) != cnt) 20051592Srgrimes goto file_err; 20061592Srgrimes byte_count += cnt; 20071592Srgrimes } 200889935Syar if (recvurg) 200989935Syar goto got_oob; 20101592Srgrimes if (cnt < 0) 20111592Srgrimes goto data_err; 20121592Srgrimes transflag = 0; 20131592Srgrimes return (0); 20141592Srgrimes 20151592Srgrimes case TYPE_E: 20161592Srgrimes reply(553, "TYPE E not implemented."); 20171592Srgrimes transflag = 0; 20181592Srgrimes return (-1); 20191592Srgrimes 20201592Srgrimes case TYPE_A: 20211592Srgrimes while ((c = getc(instr)) != EOF) { 202289935Syar if (recvurg) 202389935Syar goto got_oob; 20241592Srgrimes byte_count++; 20251592Srgrimes if (c == '\n') 20261592Srgrimes bare_lfs++; 20271592Srgrimes while (c == '\r') { 20281592Srgrimes if (ferror(outstr)) 20291592Srgrimes goto data_err; 20301592Srgrimes if ((c = getc(instr)) != '\n') { 20311592Srgrimes (void) putc ('\r', outstr); 20321592Srgrimes if (c == '\0' || c == EOF) 20331592Srgrimes goto contin2; 20341592Srgrimes } 20351592Srgrimes } 20361592Srgrimes (void) putc(c, outstr); 20371592Srgrimes contin2: ; 20381592Srgrimes } 203989935Syar if (recvurg) 204089935Syar goto got_oob; 20411592Srgrimes fflush(outstr); 20421592Srgrimes if (ferror(instr)) 20431592Srgrimes goto data_err; 20441592Srgrimes if (ferror(outstr)) 20451592Srgrimes goto file_err; 20461592Srgrimes transflag = 0; 20471592Srgrimes if (bare_lfs) { 20481592Srgrimes lreply(226, 20491592Srgrimes "WARNING! %d bare linefeeds received in ASCII mode", 20501592Srgrimes bare_lfs); 20511592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 20521592Srgrimes } 20531592Srgrimes return (0); 20541592Srgrimes default: 20551592Srgrimes reply(550, "Unimplemented TYPE %d in receive_data", type); 20561592Srgrimes transflag = 0; 20571592Srgrimes return (-1); 20581592Srgrimes } 20591592Srgrimes 20601592Srgrimesdata_err: 20611592Srgrimes transflag = 0; 20621592Srgrimes perror_reply(426, "Data Connection"); 20631592Srgrimes return (-1); 20641592Srgrimes 20651592Srgrimesfile_err: 20661592Srgrimes transflag = 0; 20671592Srgrimes perror_reply(452, "Error writing file"); 20681592Srgrimes return (-1); 206989935Syar 207089935Syargot_oob: 207189935Syar myoob(); 207289935Syar recvurg = 0; 207389935Syar transflag = 0; 207489935Syar return (-1); 20751592Srgrimes} 20761592Srgrimes 20771592Srgrimesvoid 207890148Simpstatfilecmd(char *filename) 20791592Srgrimes{ 20801592Srgrimes FILE *fin; 20811592Srgrimes int c; 20821592Srgrimes char line[LINE_MAX]; 20831592Srgrimes 208425165Sdavidn (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 20851592Srgrimes fin = ftpd_popen(line, "r"); 20861592Srgrimes lreply(211, "status of %s:", filename); 20871592Srgrimes while ((c = getc(fin)) != EOF) { 20881592Srgrimes if (c == '\n') { 20891592Srgrimes if (ferror(stdout)){ 20901592Srgrimes perror_reply(421, "control connection"); 20911592Srgrimes (void) ftpd_pclose(fin); 20921592Srgrimes dologout(1); 20931592Srgrimes /* NOTREACHED */ 20941592Srgrimes } 20951592Srgrimes if (ferror(fin)) { 20961592Srgrimes perror_reply(551, filename); 20971592Srgrimes (void) ftpd_pclose(fin); 20981592Srgrimes return; 20991592Srgrimes } 21001592Srgrimes (void) putc('\r', stdout); 21011592Srgrimes } 21021592Srgrimes (void) putc(c, stdout); 21031592Srgrimes } 21041592Srgrimes (void) ftpd_pclose(fin); 21051592Srgrimes reply(211, "End of Status"); 21061592Srgrimes} 21071592Srgrimes 21081592Srgrimesvoid 210990148Simpstatcmd(void) 21101592Srgrimes{ 211156668Sshin union sockunion *su; 21121592Srgrimes u_char *a, *p; 211399255Sume char hname[NI_MAXHOST]; 211456668Sshin int ispassive; 21151592Srgrimes 21161592Srgrimes lreply(211, "%s FTP server status:", hostname, version); 21171592Srgrimes printf(" %s\r\n", version); 21181592Srgrimes printf(" Connected to %s", remotehost); 211956668Sshin if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 212099255Sume hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 212156668Sshin if (strcmp(hname, remotehost) != 0) 212256668Sshin printf(" (%s)", hname); 212356668Sshin } 21241592Srgrimes printf("\r\n"); 21251592Srgrimes if (logged_in) { 21261592Srgrimes if (guest) 21271592Srgrimes printf(" Logged in anonymously\r\n"); 21281592Srgrimes else 21291592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 21301592Srgrimes } else if (askpasswd) 21311592Srgrimes printf(" Waiting for password\r\n"); 21321592Srgrimes else 21331592Srgrimes printf(" Waiting for user name\r\n"); 21341592Srgrimes printf(" TYPE: %s", typenames[type]); 21351592Srgrimes if (type == TYPE_A || type == TYPE_E) 21361592Srgrimes printf(", FORM: %s", formnames[form]); 21371592Srgrimes if (type == TYPE_L) 21381592Srgrimes#if NBBY == 8 21391592Srgrimes printf(" %d", NBBY); 21401592Srgrimes#else 21411592Srgrimes printf(" %d", bytesize); /* need definition! */ 21421592Srgrimes#endif 21431592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 21441592Srgrimes strunames[stru], modenames[mode]); 21451592Srgrimes if (data != -1) 21461592Srgrimes printf(" Data connection open\r\n"); 21471592Srgrimes else if (pdata != -1) { 214856668Sshin ispassive = 1; 214956668Sshin su = &pasv_addr; 21501592Srgrimes goto printaddr; 21511592Srgrimes } else if (usedefault == 0) { 215256668Sshin ispassive = 0; 215356668Sshin su = &data_dest; 21541592Srgrimesprintaddr: 21551592Srgrimes#define UC(b) (((int) b) & 0xff) 215656668Sshin if (epsvall) { 215756668Sshin printf(" EPSV only mode (EPSV ALL)\r\n"); 215856668Sshin goto epsvonly; 215956668Sshin } 216056668Sshin 216156668Sshin /* PORT/PASV */ 216256668Sshin if (su->su_family == AF_INET) { 216356668Sshin a = (u_char *) &su->su_sin.sin_addr; 216456668Sshin p = (u_char *) &su->su_sin.sin_port; 216556668Sshin printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 216656668Sshin ispassive ? "PASV" : "PORT", 216756668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 216856668Sshin UC(p[0]), UC(p[1])); 216956668Sshin } 217056668Sshin 217156668Sshin /* LPRT/LPSV */ 217256668Sshin { 217356668Sshin int alen, af, i; 217456668Sshin 217556668Sshin switch (su->su_family) { 217656668Sshin case AF_INET: 217756668Sshin a = (u_char *) &su->su_sin.sin_addr; 217856668Sshin p = (u_char *) &su->su_sin.sin_port; 217956668Sshin alen = sizeof(su->su_sin.sin_addr); 218056668Sshin af = 4; 218156668Sshin break; 218256668Sshin case AF_INET6: 218356668Sshin a = (u_char *) &su->su_sin6.sin6_addr; 218456668Sshin p = (u_char *) &su->su_sin6.sin6_port; 218556668Sshin alen = sizeof(su->su_sin6.sin6_addr); 218656668Sshin af = 6; 218756668Sshin break; 218856668Sshin default: 218956668Sshin af = 0; 219056668Sshin break; 219156668Sshin } 219256668Sshin if (af) { 219356668Sshin printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 219456668Sshin af, alen); 219556668Sshin for (i = 0; i < alen; i++) 219656668Sshin printf("%d,", UC(a[i])); 219756668Sshin printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 219856668Sshin } 219956668Sshin } 220056668Sshin 220156668Sshinepsvonly:; 220256668Sshin /* EPRT/EPSV */ 220356668Sshin { 220456668Sshin int af; 220556668Sshin 220656668Sshin switch (su->su_family) { 220756668Sshin case AF_INET: 220856668Sshin af = 1; 220956668Sshin break; 221056668Sshin case AF_INET6: 221156668Sshin af = 2; 221256668Sshin break; 221356668Sshin default: 221456668Sshin af = 0; 221556668Sshin break; 221656668Sshin } 221756668Sshin if (af) { 221899255Sume union sockunion tmp; 221999255Sume 222099255Sume tmp = *su; 222199255Sume if (tmp.su_family == AF_INET6) 222299255Sume tmp.su_sin6.sin6_scope_id = 0; 222399255Sume if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 222456668Sshin hname, sizeof(hname) - 1, NULL, 0, 222556668Sshin NI_NUMERICHOST)) { 222656668Sshin printf(" %s |%d|%s|%d|\r\n", 222756668Sshin ispassive ? "EPSV" : "EPRT", 222899255Sume af, hname, htons(tmp.su_port)); 222956668Sshin } 223056668Sshin } 223156668Sshin } 22321592Srgrimes#undef UC 22331592Srgrimes } else 22341592Srgrimes printf(" No data connection\r\n"); 22351592Srgrimes reply(211, "End of status"); 22361592Srgrimes} 22371592Srgrimes 22381592Srgrimesvoid 223990148Simpfatalerror(char *s) 22401592Srgrimes{ 22411592Srgrimes 22421592Srgrimes reply(451, "Error in server: %s\n", s); 22431592Srgrimes reply(221, "Closing connection due to server error."); 22441592Srgrimes dologout(0); 22451592Srgrimes /* NOTREACHED */ 22461592Srgrimes} 22471592Srgrimes 22481592Srgrimesvoid 22491592Srgrimesreply(int n, const char *fmt, ...) 22501592Srgrimes{ 22511592Srgrimes va_list ap; 225290148Simp 22531592Srgrimes va_start(ap, fmt); 22541592Srgrimes (void)printf("%d ", n); 22551592Srgrimes (void)vprintf(fmt, ap); 22561592Srgrimes (void)printf("\r\n"); 22571592Srgrimes (void)fflush(stdout); 225876096Smarkm if (ftpdebug) { 22591592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 22601592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 22611592Srgrimes } 22621592Srgrimes} 22631592Srgrimes 22641592Srgrimesvoid 22651592Srgrimeslreply(int n, const char *fmt, ...) 22661592Srgrimes{ 22671592Srgrimes va_list ap; 226890148Simp 22691592Srgrimes va_start(ap, fmt); 22701592Srgrimes (void)printf("%d- ", n); 22711592Srgrimes (void)vprintf(fmt, ap); 22721592Srgrimes (void)printf("\r\n"); 22731592Srgrimes (void)fflush(stdout); 227476096Smarkm if (ftpdebug) { 22751592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 22761592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 22771592Srgrimes } 22781592Srgrimes} 22791592Srgrimes 22801592Srgrimesstatic void 228190148Simpack(char *s) 22821592Srgrimes{ 22831592Srgrimes 22841592Srgrimes reply(250, "%s command successful.", s); 22851592Srgrimes} 22861592Srgrimes 22871592Srgrimesvoid 228890148Simpnack(char *s) 22891592Srgrimes{ 22901592Srgrimes 22911592Srgrimes reply(502, "%s command not implemented.", s); 22921592Srgrimes} 22931592Srgrimes 22941592Srgrimes/* ARGSUSED */ 22951592Srgrimesvoid 229690148Simpyyerror(char *s) 22971592Srgrimes{ 22981592Srgrimes char *cp; 22991592Srgrimes 230017478Smarkm if ((cp = strchr(cbuf,'\n'))) 23011592Srgrimes *cp = '\0'; 23021592Srgrimes reply(500, "'%s': command not understood.", cbuf); 23031592Srgrimes} 23041592Srgrimes 23051592Srgrimesvoid 230690148Simpdelete(char *name) 23071592Srgrimes{ 23081592Srgrimes struct stat st; 23091592Srgrimes 23101592Srgrimes LOGCMD("delete", name); 2311100439Syar if (lstat(name, &st) < 0) { 23121592Srgrimes perror_reply(550, name); 23131592Srgrimes return; 23141592Srgrimes } 23151592Srgrimes if ((st.st_mode&S_IFMT) == S_IFDIR) { 23161592Srgrimes if (rmdir(name) < 0) { 23171592Srgrimes perror_reply(550, name); 23181592Srgrimes return; 23191592Srgrimes } 23201592Srgrimes goto done; 23211592Srgrimes } 23221592Srgrimes if (unlink(name) < 0) { 23231592Srgrimes perror_reply(550, name); 23241592Srgrimes return; 23251592Srgrimes } 23261592Srgrimesdone: 23271592Srgrimes ack("DELE"); 23281592Srgrimes} 23291592Srgrimes 23301592Srgrimesvoid 233190148Simpcwd(char *path) 23321592Srgrimes{ 23331592Srgrimes 23341592Srgrimes if (chdir(path) < 0) 23351592Srgrimes perror_reply(550, path); 23361592Srgrimes else 23371592Srgrimes ack("CWD"); 23381592Srgrimes} 23391592Srgrimes 23401592Srgrimesvoid 234190148Simpmakedir(char *name) 23421592Srgrimes{ 2343100878Syar char *s; 23441592Srgrimes 23451592Srgrimes LOGCMD("mkdir", name); 234699195Smdodd if (guest && noguestmkd) 234799195Smdodd reply(550, "%s: permission denied", name); 234899195Smdodd else if (mkdir(name, 0777) < 0) 23491592Srgrimes perror_reply(550, name); 2350100878Syar else { 2351100878Syar if ((s = doublequote(name)) == NULL) 2352100878Syar fatalerror("Ran out of memory."); 2353100878Syar reply(257, "\"%s\" directory created.", s); 2354100878Syar free(s); 2355100878Syar } 23561592Srgrimes} 23571592Srgrimes 23581592Srgrimesvoid 235990148Simpremovedir(char *name) 23601592Srgrimes{ 23611592Srgrimes 23621592Srgrimes LOGCMD("rmdir", name); 23631592Srgrimes if (rmdir(name) < 0) 23641592Srgrimes perror_reply(550, name); 23651592Srgrimes else 23661592Srgrimes ack("RMD"); 23671592Srgrimes} 23681592Srgrimes 23691592Srgrimesvoid 237090148Simppwd(void) 23711592Srgrimes{ 2372100486Syar char *s, path[MAXPATHLEN + 1]; 23731592Srgrimes 23741592Srgrimes if (getwd(path) == (char *)NULL) 23751592Srgrimes reply(550, "%s.", path); 2376100486Syar else { 2377100486Syar if ((s = doublequote(path)) == NULL) 2378100486Syar fatalerror("Ran out of memory."); 2379100486Syar reply(257, "\"%s\" is current directory.", s); 2380100486Syar free(s); 2381100486Syar } 23821592Srgrimes} 23831592Srgrimes 23841592Srgrimeschar * 238590148Simprenamefrom(char *name) 23861592Srgrimes{ 23871592Srgrimes struct stat st; 23881592Srgrimes 2389100439Syar if (lstat(name, &st) < 0) { 23901592Srgrimes perror_reply(550, name); 23911592Srgrimes return ((char *)0); 23921592Srgrimes } 23931592Srgrimes reply(350, "File exists, ready for destination name"); 23941592Srgrimes return (name); 23951592Srgrimes} 23961592Srgrimes 23971592Srgrimesvoid 239890148Simprenamecmd(char *from, char *to) 23991592Srgrimes{ 240017433Spst struct stat st; 24011592Srgrimes 24021592Srgrimes LOGCMD2("rename", from, to); 240317433Spst 240417433Spst if (guest && (stat(to, &st) == 0)) { 240517433Spst reply(550, "%s: permission denied", to); 240617433Spst return; 240717433Spst } 240817433Spst 24091592Srgrimes if (rename(from, to) < 0) 24101592Srgrimes perror_reply(550, "rename"); 24111592Srgrimes else 24121592Srgrimes ack("RNTO"); 24131592Srgrimes} 24141592Srgrimes 24151592Srgrimesstatic void 241690148Simpdolog(struct sockaddr *who) 24171592Srgrimes{ 241856668Sshin int error; 24191592Srgrimes 242056668Sshin realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 242156668Sshin 24221592Srgrimes#ifdef SETPROCTITLE 242325283Sdavidn#ifdef VIRTUAL_HOSTING 242425283Sdavidn if (thishost != firsthost) 242525283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 242625283Sdavidn remotehost, hostname); 242725283Sdavidn else 242825283Sdavidn#endif 242925283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected", 243025283Sdavidn remotehost); 243113139Speter setproctitle("%s", proctitle); 24321592Srgrimes#endif /* SETPROCTITLE */ 24331592Srgrimes 243425283Sdavidn if (logging) { 243525283Sdavidn#ifdef VIRTUAL_HOSTING 243625283Sdavidn if (thishost != firsthost) 243725283Sdavidn syslog(LOG_INFO, "connection from %s (to %s)", 243825283Sdavidn remotehost, hostname); 243925283Sdavidn else 244025283Sdavidn#endif 244156668Sshin { 244256668Sshin char who_name[MAXHOSTNAMELEN]; 244356668Sshin 244456668Sshin error = getnameinfo(who, who->sa_len, 244556668Sshin who_name, sizeof(who_name) - 1, 244699255Sume NULL, 0, NI_NUMERICHOST); 244733782Seivind syslog(LOG_INFO, "connection from %s (%s)", remotehost, 244856668Sshin error == 0 ? who_name : ""); 244956668Sshin } 245025283Sdavidn } 24511592Srgrimes} 24521592Srgrimes 24531592Srgrimes/* 24541592Srgrimes * Record logout in wtmp file 24551592Srgrimes * and exit with supplied status. 24561592Srgrimes */ 24571592Srgrimesvoid 245890148Simpdologout(int status) 24591592Srgrimes{ 246022057Sdg /* 246122057Sdg * Prevent reception of SIGURG from resulting in a resumption 246222057Sdg * back to the main program loop. 246322058Sdg */ 246422057Sdg transflag = 0; 24651592Srgrimes 24661592Srgrimes if (logged_in) { 24671592Srgrimes (void) seteuid((uid_t)0); 246889920Sume ftpd_logwtmp(ttyline, "", NULL); 24691592Srgrimes } 24701592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 24711592Srgrimes _exit(status); 24721592Srgrimes} 24731592Srgrimes 24741592Srgrimesstatic void 247590148Simpsigurg(int signo) 24761592Srgrimes{ 247789935Syar 247889935Syar recvurg = 1; 247989935Syar} 248089935Syar 248189935Syarstatic void 248290148Simpmyoob(void) 248389935Syar{ 24841592Srgrimes char *cp; 24851592Srgrimes 24861592Srgrimes /* only process if transfer occurring */ 24871592Srgrimes if (!transflag) 24881592Srgrimes return; 24891592Srgrimes cp = tmpline; 24901592Srgrimes if (getline(cp, 7, stdin) == NULL) { 24911592Srgrimes reply(221, "You could at least say goodbye."); 24921592Srgrimes dologout(0); 24931592Srgrimes } 24941592Srgrimes upper(cp); 24951592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 24961592Srgrimes tmpline[0] = '\0'; 24971592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 24981592Srgrimes reply(226, "Abort successful"); 24991592Srgrimes } 25001592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 250151192Smharo tmpline[0] = '\0'; 25021592Srgrimes if (file_size != (off_t) -1) 25031592Srgrimes reply(213, "Status: %qd of %qd bytes transferred", 25041592Srgrimes byte_count, file_size); 25051592Srgrimes else 25061592Srgrimes reply(213, "Status: %qd bytes transferred", byte_count); 25071592Srgrimes } 25081592Srgrimes} 25091592Srgrimes 25101592Srgrimes/* 25111592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 25121592Srgrimes * the PASV command in RFC959. However, it has been blessed as 25131592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 25141592Srgrimes * with Rick Adams on 25 Jan 89. 25151592Srgrimes */ 25161592Srgrimesvoid 251790148Simppassive(void) 25181592Srgrimes{ 2519100615Syar int len, on; 25201592Srgrimes char *p, *a; 25211592Srgrimes 252217433Spst if (pdata >= 0) /* close old port if one set */ 252317433Spst close(pdata); 252417433Spst 252556668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 25261592Srgrimes if (pdata < 0) { 25271592Srgrimes perror_reply(425, "Can't open passive connection"); 25281592Srgrimes return; 25291592Srgrimes } 2530100615Syar on = 1; 2531100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2532100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 25339933Spst 253417433Spst (void) seteuid((uid_t)0); 253517433Spst 253619903Spst#ifdef IP_PORTRANGE 253756668Sshin if (ctrl_addr.su_family == AF_INET) { 2538100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2539100615Syar : IP_PORTRANGE_DEFAULT; 254019903Spst 254119903Spst if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2542100612Syar &on, sizeof(on)) < 0) 254319903Spst goto pasv_error; 25441592Srgrimes } 254519903Spst#endif 254660929Snsayer#ifdef IPV6_PORTRANGE 254760929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2548100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2549100615Syar : IPV6_PORTRANGE_DEFAULT; 25509933Spst 255160929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2552100612Syar &on, sizeof(on)) < 0) 255360929Snsayer goto pasv_error; 255460929Snsayer } 255560929Snsayer#endif 255660929Snsayer 255716033Speter pasv_addr = ctrl_addr; 255856668Sshin pasv_addr.su_port = 0; 255956668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 256016033Speter goto pasv_error; 256117433Spst 256216033Speter (void) seteuid((uid_t)pw->pw_uid); 256316033Speter 25641592Srgrimes len = sizeof(pasv_addr); 25651592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 25661592Srgrimes goto pasv_error; 25671592Srgrimes if (listen(pdata, 1) < 0) 25681592Srgrimes goto pasv_error; 256956668Sshin if (pasv_addr.su_family == AF_INET) 257056668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 257156668Sshin else if (pasv_addr.su_family == AF_INET6 && 257256668Sshin IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 257356668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 257456668Sshin else 257556668Sshin goto pasv_error; 257656668Sshin 257756668Sshin p = (char *) &pasv_addr.su_port; 25781592Srgrimes 25791592Srgrimes#define UC(b) (((int) b) & 0xff) 25801592Srgrimes 25811592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 25821592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 25831592Srgrimes return; 25841592Srgrimes 25851592Srgrimespasv_error: 258617433Spst (void) seteuid((uid_t)pw->pw_uid); 25871592Srgrimes (void) close(pdata); 25881592Srgrimes pdata = -1; 25891592Srgrimes perror_reply(425, "Can't open passive connection"); 25901592Srgrimes return; 25911592Srgrimes} 25921592Srgrimes 25931592Srgrimes/* 259456668Sshin * Long Passive defined in RFC 1639. 259556668Sshin * 228 Entering Long Passive Mode 259656668Sshin * (af, hal, h1, h2, h3,..., pal, p1, p2...) 259756668Sshin */ 259856668Sshin 259956668Sshinvoid 260090148Simplong_passive(char *cmd, int pf) 260156668Sshin{ 2602100615Syar int len, on; 260356668Sshin char *p, *a; 260456668Sshin 260556668Sshin if (pdata >= 0) /* close old port if one set */ 260656668Sshin close(pdata); 260756668Sshin 260856668Sshin if (pf != PF_UNSPEC) { 260956668Sshin if (ctrl_addr.su_family != pf) { 261056668Sshin switch (ctrl_addr.su_family) { 261156668Sshin case AF_INET: 261256668Sshin pf = 1; 261356668Sshin break; 261456668Sshin case AF_INET6: 261556668Sshin pf = 2; 261656668Sshin break; 261756668Sshin default: 261856668Sshin pf = 0; 261956668Sshin break; 262056668Sshin } 262156668Sshin /* 262256668Sshin * XXX 262356668Sshin * only EPRT/EPSV ready clients will understand this 262456668Sshin */ 262556668Sshin if (strcmp(cmd, "EPSV") == 0 && pf) { 262656668Sshin reply(522, "Network protocol mismatch, " 262756668Sshin "use (%d)", pf); 262856668Sshin } else 262956668Sshin reply(501, "Network protocol mismatch"); /*XXX*/ 263056668Sshin 263156668Sshin return; 263256668Sshin } 263356668Sshin } 263456668Sshin 263556668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 263656668Sshin if (pdata < 0) { 263756668Sshin perror_reply(425, "Can't open passive connection"); 263856668Sshin return; 263956668Sshin } 2640100615Syar on = 1; 2641100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2642100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 264356668Sshin 264456668Sshin (void) seteuid((uid_t)0); 264556668Sshin 264656668Sshin pasv_addr = ctrl_addr; 264756668Sshin pasv_addr.su_port = 0; 264856668Sshin len = pasv_addr.su_len; 264956668Sshin 265060929Snsayer#ifdef IP_PORTRANGE 265160929Snsayer if (ctrl_addr.su_family == AF_INET) { 2652100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2653100615Syar : IP_PORTRANGE_DEFAULT; 265460929Snsayer 265560929Snsayer if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2656100612Syar &on, sizeof(on)) < 0) 265760929Snsayer goto pasv_error; 265860929Snsayer } 265960929Snsayer#endif 266060929Snsayer#ifdef IPV6_PORTRANGE 266160929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2662100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2663100615Syar : IPV6_PORTRANGE_DEFAULT; 266460929Snsayer 266560929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2666100612Syar &on, sizeof(on)) < 0) 266760929Snsayer goto pasv_error; 266860929Snsayer } 266960929Snsayer#endif 267060929Snsayer 267156668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 267256668Sshin goto pasv_error; 267356668Sshin 267456668Sshin (void) seteuid((uid_t)pw->pw_uid); 267556668Sshin 267656668Sshin if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 267756668Sshin goto pasv_error; 267856668Sshin if (listen(pdata, 1) < 0) 267956668Sshin goto pasv_error; 268056668Sshin 268156668Sshin#define UC(b) (((int) b) & 0xff) 268256668Sshin 268356668Sshin if (strcmp(cmd, "LPSV") == 0) { 268456668Sshin p = (char *)&pasv_addr.su_port; 268556668Sshin switch (pasv_addr.su_family) { 268656668Sshin case AF_INET: 268756668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 268856668Sshin v4_reply: 268956668Sshin reply(228, 269056668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 269156668Sshin 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 269256668Sshin 2, UC(p[0]), UC(p[1])); 269356668Sshin return; 269456668Sshin case AF_INET6: 269556668Sshin if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 269656668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 269756668Sshin goto v4_reply; 269856668Sshin } 269956668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr; 270056668Sshin reply(228, 270156668Sshin"Entering Long Passive Mode " 270256668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 270356668Sshin 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 270456668Sshin UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 270556668Sshin UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 270656668Sshin UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 270756668Sshin 2, UC(p[0]), UC(p[1])); 270856668Sshin return; 270956668Sshin } 271056668Sshin } else if (strcmp(cmd, "EPSV") == 0) { 271156668Sshin switch (pasv_addr.su_family) { 271256668Sshin case AF_INET: 271356668Sshin case AF_INET6: 271456668Sshin reply(229, "Entering Extended Passive Mode (|||%d|)", 271556668Sshin ntohs(pasv_addr.su_port)); 271656668Sshin return; 271756668Sshin } 271856668Sshin } else { 271956668Sshin /* more proper error code? */ 272056668Sshin } 272156668Sshin 272256668Sshinpasv_error: 272356668Sshin (void) seteuid((uid_t)pw->pw_uid); 272456668Sshin (void) close(pdata); 272556668Sshin pdata = -1; 272656668Sshin perror_reply(425, "Can't open passive connection"); 272756668Sshin return; 272856668Sshin} 272956668Sshin 273056668Sshin/* 2731101537Syar * Generate unique name for file with basename "local" 2732101537Syar * and open the file in order to avoid possible races. 2733101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 2734101537Syar * Return descriptor to the file, set "name" to its name. 2735101537Syar * 27361592Srgrimes * Generates failure reply on error. 27371592Srgrimes */ 2738101537Syarstatic int 2739101537Syarguniquefd(char *local, char **name) 27401592Srgrimes{ 27411592Srgrimes static char new[MAXPATHLEN]; 27421592Srgrimes struct stat st; 2743101537Syar char *cp; 27441592Srgrimes int count; 2745101537Syar int fd; 27461592Srgrimes 27471592Srgrimes cp = strrchr(local, '/'); 27481592Srgrimes if (cp) 27491592Srgrimes *cp = '\0'; 27501592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 27511592Srgrimes perror_reply(553, cp ? local : "."); 2752101537Syar return (-1); 27531592Srgrimes } 2754101537Syar if (cp) { 2755101537Syar /* 2756101537Syar * Let not overwrite dirname with counter suffix. 2757101537Syar * -4 is for /nn\0 2758101537Syar * In this extreme case dot won't be put in front of suffix. 2759101537Syar */ 2760101537Syar if (strlen(local) > sizeof(new) - 4) { 2761101537Syar reply(553, "Pathname too long"); 2762101537Syar return (-1); 2763101537Syar } 27641592Srgrimes *cp = '/'; 2765101537Syar } 276631973Simp /* -4 is for the .nn<null> we put on the end below */ 276731973Simp (void) snprintf(new, sizeof(new) - 4, "%s", local); 27681592Srgrimes cp = new + strlen(new); 2769101537Syar /* 2770101537Syar * Don't generate dotfile unless requested explicitly. 2771101537Syar * This covers the case when basename gets truncated off 2772101537Syar * by buffer size. 2773101537Syar */ 2774101537Syar if (cp > new && cp[-1] != '/') 2775101537Syar *cp++ = '.'; 2776101537Syar for (count = 0; count < 100; count++) { 2777101537Syar /* At count 0 try unmodified name */ 2778101537Syar if (count) 2779101537Syar (void)sprintf(cp, "%d", count); 2780101537Syar if ((fd = open(count ? new : local, 2781101537Syar O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 2782101537Syar *name = count ? new : local; 2783101537Syar return (fd); 2784101537Syar } 27851592Srgrimes } 27861592Srgrimes reply(452, "Unique file name cannot be created."); 2787101537Syar return (-1); 27881592Srgrimes} 27891592Srgrimes 27901592Srgrimes/* 27911592Srgrimes * Format and send reply containing system error number. 27921592Srgrimes */ 27931592Srgrimesvoid 279490148Simpperror_reply(int code, char *string) 27951592Srgrimes{ 27961592Srgrimes 27971592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 27981592Srgrimes} 27991592Srgrimes 28001592Srgrimesstatic char *onefile[] = { 28011592Srgrimes "", 28021592Srgrimes 0 28031592Srgrimes}; 28041592Srgrimes 28051592Srgrimesvoid 280690148Simpsend_file_list(char *whichf) 28071592Srgrimes{ 28081592Srgrimes struct stat st; 28091592Srgrimes DIR *dirp = NULL; 28101592Srgrimes struct dirent *dir; 28111592Srgrimes FILE *dout = NULL; 28121592Srgrimes char **dirlist, *dirname; 28131592Srgrimes int simple = 0; 28141592Srgrimes int freeglob = 0; 28151592Srgrimes glob_t gl; 28161592Srgrimes 28171592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 2818100222Smikeh int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 28191592Srgrimes 28201592Srgrimes memset(&gl, 0, sizeof(gl)); 282174470Sjlemon gl.gl_matchc = MAXGLOBARGS; 282280525Smikeh flags |= GLOB_LIMIT; 28231592Srgrimes freeglob = 1; 28241592Srgrimes if (glob(whichf, flags, 0, &gl)) { 28251592Srgrimes reply(550, "not found"); 28261592Srgrimes goto out; 28271592Srgrimes } else if (gl.gl_pathc == 0) { 28281592Srgrimes errno = ENOENT; 28291592Srgrimes perror_reply(550, whichf); 28301592Srgrimes goto out; 28311592Srgrimes } 28321592Srgrimes dirlist = gl.gl_pathv; 28331592Srgrimes } else { 28341592Srgrimes onefile[0] = whichf; 28351592Srgrimes dirlist = onefile; 28361592Srgrimes simple = 1; 28371592Srgrimes } 28381592Srgrimes 283917478Smarkm while ((dirname = *dirlist++)) { 28401592Srgrimes if (stat(dirname, &st) < 0) { 28411592Srgrimes /* 28421592Srgrimes * If user typed "ls -l", etc, and the client 28431592Srgrimes * used NLST, do what the user meant. 28441592Srgrimes */ 28451592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 28461592Srgrimes transflag == 0) { 284725165Sdavidn retrieve(_PATH_LS " %s", dirname); 28481592Srgrimes goto out; 28491592Srgrimes } 28501592Srgrimes perror_reply(550, whichf); 28511592Srgrimes if (dout != NULL) { 28521592Srgrimes (void) fclose(dout); 28531592Srgrimes transflag = 0; 28541592Srgrimes data = -1; 28551592Srgrimes pdata = -1; 28561592Srgrimes } 28571592Srgrimes goto out; 28581592Srgrimes } 28591592Srgrimes 28601592Srgrimes if (S_ISREG(st.st_mode)) { 28611592Srgrimes if (dout == NULL) { 28621592Srgrimes dout = dataconn("file list", (off_t)-1, "w"); 28631592Srgrimes if (dout == NULL) 28641592Srgrimes goto out; 28651592Srgrimes transflag++; 28661592Srgrimes } 28671592Srgrimes fprintf(dout, "%s%s\n", dirname, 28681592Srgrimes type == TYPE_A ? "\r" : ""); 28691592Srgrimes byte_count += strlen(dirname) + 1; 28701592Srgrimes continue; 28711592Srgrimes } else if (!S_ISDIR(st.st_mode)) 28721592Srgrimes continue; 28731592Srgrimes 28741592Srgrimes if ((dirp = opendir(dirname)) == NULL) 28751592Srgrimes continue; 28761592Srgrimes 28771592Srgrimes while ((dir = readdir(dirp)) != NULL) { 28781592Srgrimes char nbuf[MAXPATHLEN]; 28791592Srgrimes 288089935Syar if (recvurg) { 288189935Syar myoob(); 288289935Syar recvurg = 0; 288389935Syar transflag = 0; 288489935Syar goto out; 288589935Syar } 288689935Syar 28871592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 28881592Srgrimes continue; 28891592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 28901592Srgrimes dir->d_namlen == 2) 28911592Srgrimes continue; 28921592Srgrimes 289399213Smaxim snprintf(nbuf, sizeof(nbuf), 289431973Simp "%s/%s", dirname, dir->d_name); 28951592Srgrimes 28961592Srgrimes /* 28971592Srgrimes * We have to do a stat to insure it's 28981592Srgrimes * not a directory or special file. 28991592Srgrimes */ 29001592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 29011592Srgrimes S_ISREG(st.st_mode))) { 29021592Srgrimes if (dout == NULL) { 29031592Srgrimes dout = dataconn("file list", (off_t)-1, 29041592Srgrimes "w"); 29051592Srgrimes if (dout == NULL) 29061592Srgrimes goto out; 29071592Srgrimes transflag++; 29081592Srgrimes } 29091592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 29101592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 29111592Srgrimes type == TYPE_A ? "\r" : ""); 29121592Srgrimes else 29131592Srgrimes fprintf(dout, "%s%s\n", nbuf, 29141592Srgrimes type == TYPE_A ? "\r" : ""); 29151592Srgrimes byte_count += strlen(nbuf) + 1; 29161592Srgrimes } 29171592Srgrimes } 29181592Srgrimes (void) closedir(dirp); 29191592Srgrimes } 29201592Srgrimes 29211592Srgrimes if (dout == NULL) 29221592Srgrimes reply(550, "No files found."); 29231592Srgrimes else if (ferror(dout) != 0) 29241592Srgrimes perror_reply(550, "Data connection"); 29251592Srgrimes else 29261592Srgrimes reply(226, "Transfer complete."); 29271592Srgrimes 29281592Srgrimes transflag = 0; 29291592Srgrimes if (dout != NULL) 29301592Srgrimes (void) fclose(dout); 29311592Srgrimes data = -1; 29321592Srgrimes pdata = -1; 29331592Srgrimesout: 29341592Srgrimes if (freeglob) { 29351592Srgrimes freeglob = 0; 29361592Srgrimes globfree(&gl); 29371592Srgrimes } 29381592Srgrimes} 29391592Srgrimes 294015196Sdgvoid 294190148Simpreapchild(int signo) 294215196Sdg{ 294315196Sdg while (wait3(NULL, WNOHANG, NULL) > 0); 294415196Sdg} 294515196Sdg 294613139Speter#ifdef OLD_SETPROCTITLE 29471592Srgrimes/* 29481592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 29491592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 29501592Srgrimes * have much of an environment or arglist to overwrite. 29511592Srgrimes */ 29521592Srgrimesvoid 29531592Srgrimessetproctitle(const char *fmt, ...) 29541592Srgrimes{ 29551592Srgrimes int i; 29561592Srgrimes va_list ap; 29571592Srgrimes char *p, *bp, ch; 29581592Srgrimes char buf[LINE_MAX]; 29591592Srgrimes 29601592Srgrimes va_start(ap, fmt); 29611592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 29621592Srgrimes 29631592Srgrimes /* make ps print our process name */ 29641592Srgrimes p = Argv[0]; 29651592Srgrimes *p++ = '-'; 29661592Srgrimes 29671592Srgrimes i = strlen(buf); 29681592Srgrimes if (i > LastArgv - p - 2) { 29691592Srgrimes i = LastArgv - p - 2; 29701592Srgrimes buf[i] = '\0'; 29711592Srgrimes } 29721592Srgrimes bp = buf; 29731592Srgrimes while (ch = *bp++) 29741592Srgrimes if (ch != '\n' && ch != '\r') 29751592Srgrimes *p++ = ch; 29761592Srgrimes while (p < LastArgv) 29771592Srgrimes *p++ = ' '; 29781592Srgrimes} 297913139Speter#endif /* OLD_SETPROCTITLE */ 29806740Sguido 298117433Spststatic void 298290148Simplogxfer(char *name, off_t size, time_t start) 29836740Sguido{ 29846740Sguido char buf[1024]; 29856740Sguido char path[MAXPATHLEN + 1]; 298636612Sjb time_t now; 29876740Sguido 29886740Sguido if (statfd >= 0 && getwd(path) != NULL) { 29896740Sguido time(&now); 299082792Sache snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%qd!%ld\n", 29916740Sguido ctime(&now)+4, ident, remotehost, 299282792Sache path, name, (long long)size, 299382792Sache (long)(now - start + (now == start))); 29946740Sguido write(statfd, buf, strlen(buf)); 29956740Sguido } 29966740Sguido} 2997100486Syar 2998100486Syarstatic char * 2999100486Syardoublequote(char *s) 3000100486Syar{ 3001100486Syar int n; 3002100486Syar char *p, *s2; 3003100486Syar 3004100486Syar for (p = s, n = 0; *p; p++) 3005100486Syar if (*p == '"') 3006100486Syar n++; 3007100486Syar 3008100486Syar if ((s2 = malloc(p - s + n + 1)) == NULL) 3009100486Syar return (NULL); 3010100486Syar 3011100486Syar for (p = s2; *s; s++, p++) { 3012100486Syar if ((*p = *s) == '"') 3013100486Syar *(++p) = '"'; 3014100486Syar } 3015100486Syar *p = '\0'; 3016100486Syar 3017100486Syar return (s2); 3018100486Syar} 3019