ftpd.c revision 129170
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 129170 2004-05-13 05:36:38Z tjr $"; 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 ctrl_addr; 11056668Sshinunion sockunion data_source; 11156668Sshinunion sockunion data_dest; 11256668Sshinunion sockunion his_addr; 11356668Sshinunion sockunion pasv_addr; 1141592Srgrimes 11515196Sdgint daemon_mode; 1161592Srgrimesint data; 117109742Syarint dataport; 118110037Syarint hostinfo = 1; /* print host-specific info in messages */ 1191592Srgrimesint logged_in; 1201592Srgrimesstruct passwd *pw; 121110036Syarchar *homedir; 12276096Smarkmint ftpdebug; 1231592Srgrimesint timeout = 900; /* timeout after 15 minutes of inactivity */ 1241592Srgrimesint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 1251592Srgrimesint logging; 1269933Spstint restricted_data_ports = 1; 12717435Spstint paranoid = 1; /* be extra careful about security */ 12820042Storstenbint anon_only = 0; /* Only anonymous ftp allowed */ 1291592Srgrimesint guest; 13017435Spstint dochroot; 131102311Syarint dowtmp = 1; 1326740Sguidoint stats; 1336740Sguidoint statfd = -1; 1341592Srgrimesint type; 1351592Srgrimesint form; 1361592Srgrimesint stru; /* avoid C keyword */ 1371592Srgrimesint mode; 1381592Srgrimesint usedefault = 1; /* for data transfers */ 1391592Srgrimesint pdata = -1; /* for passive mode */ 14070102Sphkint readonly=0; /* Server is in readonly mode. */ 14170102Sphkint noepsv=0; /* EPSV command is disabled. */ 14282460Snikint noretr=0; /* RETR command is disabled. */ 14382796Ssheldonhint noguestretr=0; /* RETR command is disabled for anon users. */ 14499195Smdoddint noguestmkd=0; /* MKD command is disabled for anon users. */ 145101537Syarint noguestmod=1; /* anon users may not modify existing files. */ 14682460Snik 14789935Syarstatic volatile sig_atomic_t recvurg; 1481592Srgrimessig_atomic_t transflag; 1491592Srgrimesoff_t file_size; 1501592Srgrimesoff_t byte_count; 1511592Srgrimes#if !defined(CMASK) || CMASK == 0 1521592Srgrimes#undef CMASK 1531592Srgrimes#define CMASK 027 1541592Srgrimes#endif 1551592Srgrimesint defumask = CMASK; /* default umask value */ 1561592Srgrimeschar tmpline[7]; 15727650Sdavidnchar *hostname; 15878153Sddint epsvall = 0; 15978153Sdd 16025283Sdavidn#ifdef VIRTUAL_HOSTING 16125283Sdavidnchar *ftpuser; 16225283Sdavidn 16325283Sdavidnstatic struct ftphost { 16425283Sdavidn struct ftphost *next; 16557124Sshin struct addrinfo *hostinfo; 16625283Sdavidn char *hostname; 16725283Sdavidn char *anonuser; 16825283Sdavidn char *statfile; 16925283Sdavidn char *welcome; 17025283Sdavidn char *loginmsg; 17125283Sdavidn} *thishost, *firsthost; 17225283Sdavidn 17325283Sdavidn#endif 17445422Sbrianchar remotehost[MAXHOSTNAMELEN]; 1756740Sguidochar *ident = NULL; 17617435Spst 17717435Spststatic char ttyline[20]; 17817435Spstchar *tty = ttyline; /* for klogin */ 17917435Spst 18074874Smarkm#ifdef USE_PAM 18190148Simpstatic int auth_pam(struct passwd**, const char*); 18274874Smarkmpam_handle_t *pamh = NULL; 18388763Sache#endif 18479469Smarkm 18579469Smarkmstatic struct opie opiedata; 18679469Smarkmstatic char opieprompt[OPIE_CHALLENGE_MAX+1]; 18788763Sachestatic int pwok; 18817478Smarkm 18917483Sjulianchar *pid_file = NULL; 19017483Sjulian 1911592Srgrimes/* 19274470Sjlemon * Limit number of pathnames that glob can return. 19374470Sjlemon * A limit of 0 indicates the number of pathnames is unlimited. 19474470Sjlemon */ 19574470Sjlemon#define MAXGLOBARGS 16384 19674470Sjlemon# 19774470Sjlemon 19874470Sjlemon/* 1991592Srgrimes * Timeout intervals for retrying connections 2001592Srgrimes * to hosts that don't accept PORT cmds. This 2011592Srgrimes * is a kludge, but given the problems with TCP... 2021592Srgrimes */ 2031592Srgrimes#define SWAITMAX 90 /* wait at most 90 seconds */ 2041592Srgrimes#define SWAITINT 5 /* interval between retries */ 2051592Srgrimes 2061592Srgrimesint swaitmax = SWAITMAX; 2071592Srgrimesint swaitint = SWAITINT; 2081592Srgrimes 2091592Srgrimes#ifdef SETPROCTITLE 21013139Speter#ifdef OLD_SETPROCTITLE 2111592Srgrimeschar **Argv = NULL; /* pointer to argument vector */ 2121592Srgrimeschar *LastArgv = NULL; /* end of argv */ 21313139Speter#endif /* OLD_SETPROCTITLE */ 2141592Srgrimeschar proctitle[LINE_MAX]; /* initial part of title */ 2151592Srgrimes#endif /* SETPROCTITLE */ 2161592Srgrimes 2171592Srgrimes#define LOGCMD(cmd, file) \ 2181592Srgrimes if (logging > 1) \ 2191592Srgrimes syslog(LOG_INFO,"%s %s%s", cmd, \ 2201592Srgrimes *(file) == '/' ? "" : curdir(), file); 2211592Srgrimes#define LOGCMD2(cmd, file1, file2) \ 2221592Srgrimes if (logging > 1) \ 2231592Srgrimes syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 2241592Srgrimes *(file1) == '/' ? "" : curdir(), file1, \ 2251592Srgrimes *(file2) == '/' ? "" : curdir(), file2); 2261592Srgrimes#define LOGBYTES(cmd, file, cnt) \ 2271592Srgrimes if (logging > 1) { \ 2281592Srgrimes if (cnt == (off_t)-1) \ 2291592Srgrimes syslog(LOG_INFO,"%s %s%s", cmd, \ 2301592Srgrimes *(file) == '/' ? "" : curdir(), file); \ 2311592Srgrimes else \ 2321592Srgrimes syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 2331592Srgrimes cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 2341592Srgrimes } 2351592Srgrimes 23625283Sdavidn#ifdef VIRTUAL_HOSTING 23790148Simpstatic void inithosts(void); 23890148Simpstatic void selecthost(union sockunion *); 23925283Sdavidn#endif 24090148Simpstatic void ack(char *); 24190148Simpstatic void sigurg(int); 24290148Simpstatic void myoob(void); 243109893Syarstatic int checkuser(char *, char *, int, char **); 24490148Simpstatic FILE *dataconn(char *, off_t, char *); 24590148Simpstatic void dolog(struct sockaddr *); 24690148Simpstatic char *curdir(void); 24790148Simpstatic void end_login(void); 24890148Simpstatic FILE *getdatasock(char *); 249101537Syarstatic int guniquefd(char *, char **); 25090148Simpstatic void lostconn(int); 25190148Simpstatic void sigquit(int); 25290148Simpstatic int receive_data(FILE *, FILE *); 25390148Simpstatic int send_data(FILE *, FILE *, off_t, off_t, int); 2541592Srgrimesstatic struct passwd * 25590148Simp sgetpwnam(char *); 25690148Simpstatic char *sgetsave(char *); 25790148Simpstatic void reapchild(int); 25890148Simpstatic void logxfer(char *, off_t, time_t); 259100486Syarstatic char *doublequote(char *); 260120059Sumestatic int *socksetup(int, char *, const char *); 2611592Srgrimes 2621592Srgrimesstatic char * 26390148Simpcurdir(void) 2641592Srgrimes{ 2651592Srgrimes static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 2661592Srgrimes 2671592Srgrimes if (getcwd(path, sizeof(path)-2) == NULL) 2681592Srgrimes return (""); 2691592Srgrimes if (path[1] != '\0') /* special case for root dir. */ 2701592Srgrimes strcat(path, "/"); 2711592Srgrimes /* For guest account, skip / since it's chrooted */ 2721592Srgrimes return (guest ? path+1 : path); 2731592Srgrimes} 2741592Srgrimes 2751592Srgrimesint 27690148Simpmain(int argc, char *argv[], char **envp) 2771592Srgrimes{ 2781592Srgrimes int addrlen, ch, on = 1, tos; 2791592Srgrimes char *cp, line[LINE_MAX]; 2801592Srgrimes FILE *fd; 28156668Sshin int error; 28256668Sshin char *bindname = NULL; 283109742Syar const char *bindport = "ftp"; 28456668Sshin int family = AF_UNSPEC; 28589935Syar struct sigaction sa; 2861592Srgrimes 28736105Sache tzset(); /* in case no timezone database in ~ftp */ 28889935Syar sigemptyset(&sa.sa_mask); 28989935Syar sa.sa_flags = SA_RESTART; 29036105Sache 29113139Speter#ifdef OLD_SETPROCTITLE 2921592Srgrimes /* 2931592Srgrimes * Save start and extent of argv for setproctitle. 2941592Srgrimes */ 2951592Srgrimes Argv = argv; 2961592Srgrimes while (*envp) 2971592Srgrimes envp++; 2981592Srgrimes LastArgv = envp[-1] + strlen(envp[-1]); 29913139Speter#endif /* OLD_SETPROCTITLE */ 3001592Srgrimes 3016740Sguido 302110037Syar while ((ch = getopt(argc, argv, 303110037Syar "46a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) { 3041592Srgrimes switch (ch) { 305100717Syar case '4': 306120059Sume family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; 30715196Sdg break; 30815196Sdg 309100717Syar case '6': 310120059Sume family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; 311100717Syar break; 312100717Syar 313100717Syar case 'a': 314100717Syar bindname = optarg; 315100717Syar break; 316100717Syar 317100717Syar case 'A': 318100717Syar anon_only = 1; 319100717Syar break; 320100717Syar 3211592Srgrimes case 'd': 32276096Smarkm ftpdebug++; 3231592Srgrimes break; 3241592Srgrimes 325100717Syar case 'D': 326100717Syar daemon_mode++; 327100717Syar break; 328100717Syar 32970102Sphk case 'E': 33070102Sphk noepsv = 1; 33170102Sphk break; 33270102Sphk 333110037Syar case 'h': 334110037Syar hostinfo = 0; 335110037Syar break; 336110037Syar 3371592Srgrimes case 'l': 3381592Srgrimes logging++; /* > 1 == extra logging */ 3391592Srgrimes break; 3401592Srgrimes 341101537Syar case 'm': 342101537Syar noguestmod = 0; 343101537Syar break; 344101537Syar 345100717Syar case 'M': 346100717Syar noguestmkd = 1; 347100717Syar break; 348100717Syar 349100717Syar case 'o': 350100717Syar noretr = 1; 351100717Syar break; 352100717Syar 353100717Syar case 'O': 354100717Syar noguestretr = 1; 355100717Syar break; 356100717Syar 357100717Syar case 'p': 358100717Syar pid_file = optarg; 359100717Syar break; 360100717Syar 361109742Syar case 'P': 362109742Syar bindport = optarg; 363109742Syar break; 364109742Syar 36570102Sphk case 'r': 36670102Sphk readonly = 1; 36770102Sphk break; 36870102Sphk 36917435Spst case 'R': 37017435Spst paranoid = 0; 3719933Spst break; 3729933Spst 3736740Sguido case 'S': 37417435Spst stats++; 3756740Sguido break; 37617435Spst 37717435Spst case 't': 37817435Spst timeout = atoi(optarg); 37917435Spst if (maxtimeout < timeout) 38017435Spst maxtimeout = timeout; 38117435Spst break; 38217435Spst 383100717Syar case 'T': 384100717Syar maxtimeout = atoi(optarg); 385100717Syar if (timeout > maxtimeout) 386100717Syar timeout = maxtimeout; 38717435Spst break; 38817435Spst 3891592Srgrimes case 'u': 3901592Srgrimes { 3911592Srgrimes long val = 0; 3921592Srgrimes 3931592Srgrimes val = strtol(optarg, &optarg, 8); 3941592Srgrimes if (*optarg != '\0' || val < 0) 3951592Srgrimes warnx("bad value for -u"); 3961592Srgrimes else 3971592Srgrimes defumask = val; 3981592Srgrimes break; 3991592Srgrimes } 400100717Syar case 'U': 401100717Syar restricted_data_ports = 0; 40220042Storstenb break; 4031592Srgrimes 4041592Srgrimes case 'v': 405100720Syar ftpdebug++; 4061592Srgrimes break; 4071592Srgrimes 408102311Syar case 'W': 409102311Syar dowtmp = 0; 410102311Syar break; 411102311Syar 4121592Srgrimes default: 4131592Srgrimes warnx("unknown flag -%c ignored", optopt); 4141592Srgrimes break; 4151592Srgrimes } 4161592Srgrimes } 41715196Sdg 41825283Sdavidn#ifdef VIRTUAL_HOSTING 41925283Sdavidn inithosts(); 42025283Sdavidn#endif 4211592Srgrimes (void) freopen(_PATH_DEVNULL, "w", stderr); 42215196Sdg 42315196Sdg /* 42415196Sdg * LOG_NDELAY sets up the logging connection immediately, 42515196Sdg * necessary for anonymous ftp's that chroot and can't do it later. 42615196Sdg */ 42715196Sdg openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 42815196Sdg 42915196Sdg if (daemon_mode) { 430120059Sume int *ctl_sock, fd, maxfd = -1, nfds, i; 431120059Sume fd_set defreadfds, readfds; 432120059Sume pid_t pid; 43315196Sdg 43415196Sdg /* 43515196Sdg * Detach from parent. 43615196Sdg */ 43715196Sdg if (daemon(1, 1) < 0) { 43815196Sdg syslog(LOG_ERR, "failed to become a daemon"); 43915196Sdg exit(1); 44015196Sdg } 44189935Syar sa.sa_handler = reapchild; 44289935Syar (void)sigaction(SIGCHLD, &sa, NULL); 44356668Sshin 44415196Sdg /* 44515196Sdg * Open a socket, bind it to the FTP port, and start 44615196Sdg * listening. 44715196Sdg */ 448120059Sume ctl_sock = socksetup(family, bindname, bindport); 449120059Sume if (ctl_sock == NULL) 45015196Sdg exit(1); 451120059Sume 452120059Sume FD_ZERO(&defreadfds); 453120059Sume for (i = 1; i <= *ctl_sock; i++) { 454120059Sume FD_SET(ctl_sock[i], &defreadfds); 455120059Sume if (listen(ctl_sock[i], 32) < 0) { 456120059Sume syslog(LOG_ERR, "control listen: %m"); 457120059Sume exit(1); 458120059Sume } 459120059Sume if (maxfd < ctl_sock[i]) 460120059Sume maxfd = ctl_sock[i]; 46115196Sdg } 462120059Sume 46315196Sdg /* 46417483Sjulian * Atomically write process ID 46517483Sjulian */ 46617483Sjulian if (pid_file) 46799213Smaxim { 46817483Sjulian int fd; 46917483Sjulian char buf[20]; 47017483Sjulian 47117483Sjulian fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC 47217483Sjulian | O_NONBLOCK | O_EXLOCK, 0644); 47346078Simp if (fd < 0) { 47417483Sjulian if (errno == EAGAIN) 47517483Sjulian errx(1, "%s: file locked", pid_file); 47617483Sjulian else 47717483Sjulian err(1, "%s", pid_file); 47846078Simp } 47917483Sjulian snprintf(buf, sizeof(buf), 48017483Sjulian "%lu\n", (unsigned long) getpid()); 48117483Sjulian if (write(fd, buf, strlen(buf)) < 0) 48217483Sjulian err(1, "%s: write", pid_file); 48317483Sjulian /* Leave the pid file open and locked */ 48417483Sjulian } 48517483Sjulian /* 48615196Sdg * Loop forever accepting connection requests and forking off 48715196Sdg * children to handle them. 48815196Sdg */ 48915196Sdg while (1) { 490120059Sume FD_COPY(&defreadfds, &readfds); 491120059Sume nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); 492120059Sume if (nfds <= 0) { 493120059Sume if (nfds < 0 && errno != EINTR) 494120059Sume syslog(LOG_WARNING, "select: %m"); 495120059Sume continue; 496120059Sume } 497120059Sume 498120059Sume pid = -1; 499120059Sume for (i = 1; i <= *ctl_sock; i++) 500120059Sume if (FD_ISSET(ctl_sock[i], &readfds)) { 501120059Sume addrlen = sizeof(his_addr); 502120059Sume fd = accept(ctl_sock[i], 503120059Sume (struct sockaddr *)&his_addr, 504120059Sume &addrlen); 505120059Sume if ((pid = fork()) == 0) { 506120059Sume /* child */ 507120059Sume (void) dup2(fd, 0); 508120059Sume (void) dup2(fd, 1); 509120059Sume close(ctl_sock[i]); 510120059Sume } else 511120059Sume close(fd); 512120059Sume } 513120059Sume if (pid == 0) 51415196Sdg break; 51515196Sdg } 51615196Sdg } else { 51715196Sdg addrlen = sizeof(his_addr); 51815196Sdg if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 51915196Sdg syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 52015196Sdg exit(1); 52115196Sdg } 52215196Sdg } 52315196Sdg 52489935Syar sa.sa_handler = SIG_DFL; 52589935Syar (void)sigaction(SIGCHLD, &sa, NULL); 5261592Srgrimes 52789935Syar sa.sa_handler = sigurg; 52889935Syar sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ 52989935Syar (void)sigaction(SIGURG, &sa, NULL); 53089935Syar 53189935Syar sigfillset(&sa.sa_mask); /* block all signals in handler */ 53289935Syar sa.sa_flags = SA_RESTART; 53389935Syar sa.sa_handler = sigquit; 53489935Syar (void)sigaction(SIGHUP, &sa, NULL); 53589935Syar (void)sigaction(SIGINT, &sa, NULL); 53689935Syar (void)sigaction(SIGQUIT, &sa, NULL); 53789935Syar (void)sigaction(SIGTERM, &sa, NULL); 53889935Syar 53989935Syar sa.sa_handler = lostconn; 54089935Syar (void)sigaction(SIGPIPE, &sa, NULL); 54189935Syar 54215196Sdg addrlen = sizeof(ctrl_addr); 54315196Sdg if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 54415196Sdg syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 54515196Sdg exit(1); 54615196Sdg } 547109742Syar dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ 54825283Sdavidn#ifdef VIRTUAL_HOSTING 54925283Sdavidn /* select our identity from virtual host table */ 55056668Sshin selecthost(&ctrl_addr); 55125283Sdavidn#endif 55215196Sdg#ifdef IP_TOS 55356668Sshin if (ctrl_addr.su_family == AF_INET) 55456668Sshin { 55515196Sdg tos = IPTOS_LOWDELAY; 556100612Syar if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 557100609Syar syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); 55856668Sshin } 55915196Sdg#endif 56035482Sdg /* 56135482Sdg * Disable Nagle on the control channel so that we don't have to wait 56235482Sdg * for peer's ACK before issuing our next reply. 56335482Sdg */ 56435482Sdg if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 565100609Syar syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); 56635482Sdg 56756668Sshin data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); 56815196Sdg 56917435Spst /* set this here so klogin can use it... */ 57031973Simp (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); 57117435Spst 5721592Srgrimes /* Try to handle urgent data inline */ 5731592Srgrimes#ifdef SO_OOBINLINE 574100612Syar if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) 575100609Syar syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); 5761592Srgrimes#endif 5771592Srgrimes 5781592Srgrimes#ifdef F_SETOWN 5791592Srgrimes if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 5801592Srgrimes syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 5811592Srgrimes#endif 58256668Sshin dolog((struct sockaddr *)&his_addr); 5831592Srgrimes /* 5841592Srgrimes * Set up default state 5851592Srgrimes */ 5861592Srgrimes data = -1; 5871592Srgrimes type = TYPE_A; 5881592Srgrimes form = FORM_N; 5891592Srgrimes stru = STRU_F; 5901592Srgrimes mode = MODE_S; 5911592Srgrimes tmpline[0] = '\0'; 5921592Srgrimes 5931592Srgrimes /* If logins are disabled, print out the message. */ 5941592Srgrimes if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 5951592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 5961592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 5971592Srgrimes *cp = '\0'; 5981592Srgrimes lreply(530, "%s", line); 5991592Srgrimes } 6001592Srgrimes (void) fflush(stdout); 6011592Srgrimes (void) fclose(fd); 6021592Srgrimes reply(530, "System not available."); 6031592Srgrimes exit(0); 6041592Srgrimes } 60525283Sdavidn#ifdef VIRTUAL_HOSTING 60625283Sdavidn if ((fd = fopen(thishost->welcome, "r")) != NULL) { 60725283Sdavidn#else 6081592Srgrimes if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 60925283Sdavidn#endif 6101592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6111592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6121592Srgrimes *cp = '\0'; 6131592Srgrimes lreply(220, "%s", line); 6141592Srgrimes } 6151592Srgrimes (void) fflush(stdout); 6161592Srgrimes (void) fclose(fd); 6171592Srgrimes /* reply(220,) must follow */ 6181592Srgrimes } 61925283Sdavidn#ifndef VIRTUAL_HOSTING 62027650Sdavidn if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 62176096Smarkm fatalerror("Ran out of memory."); 62245422Sbrian (void) gethostname(hostname, MAXHOSTNAMELEN - 1); 62345422Sbrian hostname[MAXHOSTNAMELEN - 1] = '\0'; 62425283Sdavidn#endif 625110037Syar if (hostinfo) 626110037Syar reply(220, "%s FTP server (%s) ready.", hostname, version); 627110037Syar else 628110037Syar reply(220, "FTP server ready."); 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) { 693102474Syar int addrsize, 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; 781100182Syar insert = 1; 782102473Syar } else { 783106754Syar if (hrp->hostinfo && hrp->hostinfo != res) 784102473Syar freeaddrinfo(hrp->hostinfo); 785100182Syar insert = 0; /* host already in the chain */ 786102473Syar } 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 */ 806102473Syar freeaddrinfo(hrp->hostinfo); 807102473Syar if (insert) 808102473Syar free(hrp); /*not in chain, can free*/ 809102473Syar else 810102473Syar hrp->hostinfo = NULL; /*mark as blank*/ 81199877Syar goto nextline; 81256668Sshin /* NOTREACHED */ 81356668Sshin } 814100612Syar if ((hp = getipnodebyaddr(addr, addrsize, 81557124Sshin hrp->hostinfo->ai_family, 81656668Sshin &hp_error)) != NULL) { 817100182Syar if (strcmp(vhost, hp->h_name) != 0) { 81825283Sdavidn if (hp->h_aliases == NULL) 819100182Syar vhost = hp->h_name; 82025283Sdavidn else { 82125283Sdavidn i = 0; 82225283Sdavidn while (hp->h_aliases[i] && 823100182Syar strcmp(vhost, hp->h_aliases[i]) != 0) 82425283Sdavidn ++i; 82525283Sdavidn if (hp->h_aliases[i] == NULL) 826100182Syar vhost = hp->h_name; 82725283Sdavidn } 82825283Sdavidn } 82925283Sdavidn } 830102183Syar if (hrp->hostname && 831102183Syar strcmp(hrp->hostname, vhost) != 0) { 832102183Syar free(hrp->hostname); 833102183Syar hrp->hostname = NULL; 834102183Syar } 835102183Syar if (hrp->hostname == NULL && 836102473Syar (hrp->hostname = strdup(vhost)) == NULL) { 837102473Syar freeaddrinfo(hrp->hostinfo); 838102473Syar hrp->hostinfo = NULL; /* mark as blank */ 839102473Syar if (hp) 840102473Syar freehostent(hp); 841100182Syar goto nextline; 842102473Syar } 843100182Syar hrp->anonuser = anonuser; 844100182Syar hrp->statfile = statfile; 845100182Syar hrp->welcome = welcome; 846100182Syar hrp->loginmsg = loginmsg; 847100182Syar if (insert) { 848100182Syar hrp->next = NULL; 849100182Syar lhrp->next = hrp; 850100182Syar lhrp = hrp; 851100182Syar } 852100263Syar if (hp) 853100263Syar freehostent(hp); 85456668Sshin } 85599877Syarnextline: 85699877Syar if (mp) 85799877Syar free(mp); 85825283Sdavidn } 85925283Sdavidn (void) fclose(fp); 86025283Sdavidn } 86125283Sdavidn} 86225283Sdavidn 86325283Sdavidnstatic void 86490148Simpselecthost(union sockunion *su) 86525283Sdavidn{ 86625283Sdavidn struct ftphost *hrp; 86756668Sshin u_int16_t port; 86856668Sshin#ifdef INET6 86956668Sshin struct in6_addr *mapped_in6 = NULL; 87056668Sshin#endif 87157124Sshin struct addrinfo *hi; 87225283Sdavidn 87356668Sshin#ifdef INET6 87456668Sshin /* 87556668Sshin * XXX IPv4 mapped IPv6 addr consideraton, 87656668Sshin * specified in rfc2373. 87756668Sshin */ 87856668Sshin if (su->su_family == AF_INET6 && 87956668Sshin IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) 88056668Sshin mapped_in6 = &su->su_sin6.sin6_addr; 88156668Sshin#endif 88256668Sshin 88325283Sdavidn hrp = thishost = firsthost; /* default */ 88456668Sshin port = su->su_port; 88556668Sshin su->su_port = 0; 88625283Sdavidn while (hrp != NULL) { 88762100Sdavidn for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { 88857124Sshin if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { 88925283Sdavidn thishost = hrp; 89025283Sdavidn break; 89125283Sdavidn } 89256668Sshin#ifdef INET6 89356668Sshin /* XXX IPv4 mapped IPv6 addr consideraton */ 89457124Sshin if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && 89556668Sshin (memcmp(&mapped_in6->s6_addr[12], 89657124Sshin &((struct sockaddr_in *)hi->ai_addr)->sin_addr, 89756668Sshin sizeof(struct in_addr)) == 0)) { 89856668Sshin thishost = hrp; 89956668Sshin break; 90056668Sshin } 90156668Sshin#endif 90262100Sdavidn } 90362100Sdavidn hrp = hrp->next; 90425283Sdavidn } 90556668Sshin su->su_port = port; 90625283Sdavidn /* setup static variables as appropriate */ 90725283Sdavidn hostname = thishost->hostname; 90825283Sdavidn ftpuser = thishost->anonuser; 90925283Sdavidn} 91025283Sdavidn#endif 91125283Sdavidn 91225283Sdavidn/* 9131592Srgrimes * Helper function for sgetpwnam(). 9141592Srgrimes */ 9151592Srgrimesstatic char * 91690148Simpsgetsave(char *s) 9171592Srgrimes{ 9181592Srgrimes char *new = malloc((unsigned) strlen(s) + 1); 9191592Srgrimes 9201592Srgrimes if (new == NULL) { 9211592Srgrimes perror_reply(421, "Local resource failure: malloc"); 9221592Srgrimes dologout(1); 9231592Srgrimes /* NOTREACHED */ 9241592Srgrimes } 9251592Srgrimes (void) strcpy(new, s); 9261592Srgrimes return (new); 9271592Srgrimes} 9281592Srgrimes 9291592Srgrimes/* 9301592Srgrimes * Save the result of a getpwnam. Used for USER command, since 9311592Srgrimes * the data returned must not be clobbered by any other command 9321592Srgrimes * (e.g., globbing). 9331592Srgrimes */ 9341592Srgrimesstatic struct passwd * 93590148Simpsgetpwnam(char *name) 9361592Srgrimes{ 9371592Srgrimes static struct passwd save; 9381592Srgrimes struct passwd *p; 9391592Srgrimes 9401592Srgrimes if ((p = getpwnam(name)) == NULL) 9411592Srgrimes return (p); 9421592Srgrimes if (save.pw_name) { 9431592Srgrimes free(save.pw_name); 9441592Srgrimes free(save.pw_passwd); 9451592Srgrimes free(save.pw_gecos); 9461592Srgrimes free(save.pw_dir); 9471592Srgrimes free(save.pw_shell); 9481592Srgrimes } 9491592Srgrimes save = *p; 9501592Srgrimes save.pw_name = sgetsave(p->pw_name); 9511592Srgrimes save.pw_passwd = sgetsave(p->pw_passwd); 9521592Srgrimes save.pw_gecos = sgetsave(p->pw_gecos); 9531592Srgrimes save.pw_dir = sgetsave(p->pw_dir); 9541592Srgrimes save.pw_shell = sgetsave(p->pw_shell); 9551592Srgrimes return (&save); 9561592Srgrimes} 9571592Srgrimes 9581592Srgrimesstatic int login_attempts; /* number of failed login attempts */ 9591592Srgrimesstatic int askpasswd; /* had user command, ask for passwd */ 96064778Ssheldonhstatic char curname[MAXLOGNAME]; /* current USER name */ 9611592Srgrimes 9621592Srgrimes/* 9631592Srgrimes * USER command. 9641592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable; 9651592Srgrimes * sets askpasswd if a PASS command is expected. If logged in previously, 9661592Srgrimes * need to reset state. If name is "ftp" or "anonymous", the name is not in 9671592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 9681592Srgrimes * If account doesn't exist, ask for passwd anyway. Otherwise, check user 9691592Srgrimes * requesting login privileges. Disallow anyone who does not have a standard 9701592Srgrimes * shell as returned by getusershell(). Disallow anyone mentioned in the file 9711592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 9721592Srgrimes */ 9731592Srgrimesvoid 97490148Simpuser(char *name) 9751592Srgrimes{ 9761592Srgrimes char *cp, *shell; 9771592Srgrimes 9781592Srgrimes if (logged_in) { 9791592Srgrimes if (guest) { 9801592Srgrimes reply(530, "Can't change user from guest login."); 9811592Srgrimes return; 98217435Spst } else if (dochroot) { 98317435Spst reply(530, "Can't change user from chroot user."); 98417435Spst return; 9851592Srgrimes } 9861592Srgrimes end_login(); 9871592Srgrimes } 9881592Srgrimes 9891592Srgrimes guest = 0; 9901592Srgrimes if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 991109893Syar if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL) || 992109893Syar checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL)) 9931592Srgrimes reply(530, "User %s access denied.", name); 99425283Sdavidn#ifdef VIRTUAL_HOSTING 99525283Sdavidn else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { 99625283Sdavidn#else 9971592Srgrimes else if ((pw = sgetpwnam("ftp")) != NULL) { 99825283Sdavidn#endif 9991592Srgrimes guest = 1; 10001592Srgrimes askpasswd = 1; 10011592Srgrimes reply(331, 10023938Spst "Guest login ok, send your email address as password."); 10031592Srgrimes } else 10041592Srgrimes reply(530, "User %s unknown.", name); 10051592Srgrimes if (!askpasswd && logging) 10061592Srgrimes syslog(LOG_NOTICE, 10071592Srgrimes "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 10081592Srgrimes return; 10091592Srgrimes } 101020042Storstenb if (anon_only != 0) { 101120042Storstenb reply(530, "Sorry, only anonymous ftp allowed."); 101220042Storstenb return; 101320042Storstenb } 101420042Storstenb 101517478Smarkm if ((pw = sgetpwnam(name))) { 10161592Srgrimes if ((shell = pw->pw_shell) == NULL || *shell == 0) 10171592Srgrimes shell = _PATH_BSHELL; 1018124687Scharnier setusershell(); 10191592Srgrimes while ((cp = getusershell()) != NULL) 10201592Srgrimes if (strcmp(cp, shell) == 0) 10211592Srgrimes break; 10221592Srgrimes endusershell(); 10231592Srgrimes 1024109893Syar if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1, NULL)) { 10251592Srgrimes reply(530, "User %s access denied.", name); 10261592Srgrimes if (logging) 10271592Srgrimes syslog(LOG_NOTICE, 10281592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 10291592Srgrimes remotehost, name); 10301592Srgrimes pw = (struct passwd *) NULL; 10311592Srgrimes return; 10321592Srgrimes } 10331592Srgrimes } 10341592Srgrimes if (logging) 10351592Srgrimes strncpy(curname, name, sizeof(curname)-1); 103688763Sache 103788763Sache pwok = 0; 103879469Smarkm#ifdef USE_PAM 103979469Smarkm /* XXX Kluge! The conversation mechanism needs to be fixed. */ 104088763Sache#endif 104188763Sache if (opiechallenge(&opiedata, name, opieprompt) == 0) { 104288763Sache pwok = (pw != NULL) && 104388763Sache opieaccessfile(remotehost) && 104488763Sache opiealways(pw->pw_dir); 104588763Sache reply(331, "Response to %s %s for %s.", 104688763Sache opieprompt, pwok ? "requested" : "required", name); 104788763Sache } else { 104888763Sache pwok = 1; 104984146Sache reply(331, "Password required for %s.", name); 105088763Sache } 10511592Srgrimes askpasswd = 1; 10521592Srgrimes /* 10531592Srgrimes * Delay before reading passwd after first failed 10541592Srgrimes * attempt to slow down passwd-guessing programs. 10551592Srgrimes */ 10561592Srgrimes if (login_attempts) 10571592Srgrimes sleep((unsigned) login_attempts); 10581592Srgrimes} 10591592Srgrimes 10601592Srgrimes/* 1061109893Syar * Check if a user is in the file "fname", 1062109893Syar * return a pointer to a malloc'd string with the rest 1063109893Syar * of the matching line in "residue" if not NULL. 10641592Srgrimes */ 10651592Srgrimesstatic int 1066109893Syarcheckuser(char *fname, char *name, int pwset, char **residue) 10671592Srgrimes{ 10681592Srgrimes FILE *fd; 10691592Srgrimes int found = 0; 107099877Syar size_t len; 107199877Syar char *line, *mp, *p; 10721592Srgrimes 107317435Spst if ((fd = fopen(fname, "r")) != NULL) { 107499877Syar while (!found && (line = fgetln(fd, &len)) != NULL) { 107599877Syar /* skip comments */ 107699877Syar if (line[0] == '#') 107799877Syar continue; 107899877Syar if (line[len - 1] == '\n') { 107999877Syar line[len - 1] = '\0'; 108099877Syar mp = NULL; 108199877Syar } else { 108299877Syar if ((mp = malloc(len + 1)) == NULL) 108399877Syar fatalerror("Ran out of memory."); 108499877Syar memcpy(mp, line, len); 108599877Syar mp[len] = '\0'; 108699877Syar line = mp; 108799877Syar } 108899877Syar /* avoid possible leading and trailing whitespace */ 108999877Syar p = strtok(line, " \t"); 109099877Syar /* skip empty lines */ 109199877Syar if (p == NULL) 109299877Syar goto nextline; 109399877Syar /* 109499877Syar * if first chr is '@', check group membership 109599877Syar */ 109699877Syar if (p[0] == '@') { 109799877Syar int i = 0; 109899877Syar struct group *grp; 109999877Syar 1100109893Syar if (p[1] == '\0') /* single @ matches anyone */ 110199877Syar found = 1; 1102109893Syar else { 1103109893Syar if ((grp = getgrnam(p+1)) == NULL) 1104109893Syar goto nextline; 1105109893Syar /* 1106109893Syar * Check user's default group 1107109893Syar */ 1108109893Syar if (pwset && grp->gr_gid == pw->pw_gid) 1109109893Syar found = 1; 1110109893Syar /* 1111109893Syar * Check supplementary groups 1112109893Syar */ 1113109893Syar while (!found && grp->gr_mem[i]) 1114109893Syar found = strcmp(name, 1115109893Syar grp->gr_mem[i++]) 1116109893Syar == 0; 1117109893Syar } 11181592Srgrimes } 111999877Syar /* 112099877Syar * Otherwise, just check for username match 112199877Syar */ 112299877Syar else 112399877Syar found = strcmp(p, name) == 0; 1124109893Syar /* 1125109893Syar * Save the rest of line to "residue" if matched 1126109893Syar */ 1127109893Syar if (found && residue) { 1128109938Syar if ((p = strtok(NULL, "")) != NULL) 1129109938Syar p += strspn(p, " \t"); 1130109938Syar if (p && *p) { 1131109893Syar if ((*residue = strdup(p)) == NULL) 1132109893Syar fatalerror("Ran out of memory."); 1133109893Syar } else 1134109893Syar *residue = NULL; 1135109893Syar } 113699877Syarnextline: 113799877Syar if (mp) 113899877Syar free(mp); 113999877Syar } 11401592Srgrimes (void) fclose(fd); 11411592Srgrimes } 11421592Srgrimes return (found); 11431592Srgrimes} 11441592Srgrimes 11451592Srgrimes/* 11461592Srgrimes * Terminate login as previous user, if any, resetting state; 11471592Srgrimes * used when USER command is given or login fails. 11481592Srgrimes */ 11491592Srgrimesstatic void 115090148Simpend_login(void) 11511592Srgrimes{ 115274874Smarkm#ifdef USE_PAM 115374874Smarkm int e; 115474874Smarkm#endif 11551592Srgrimes 11561592Srgrimes (void) seteuid((uid_t)0); 1157102311Syar if (logged_in && dowtmp) 115889920Sume ftpd_logwtmp(ttyline, "", NULL); 11591592Srgrimes pw = NULL; 116025101Sdavidn#ifdef LOGIN_CAP 116125101Sdavidn setusercontext(NULL, getpwuid(0), (uid_t)0, 1162105877Srwatson LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK| 1163105877Srwatson LOGIN_SETMAC); 116425101Sdavidn#endif 116574874Smarkm#ifdef USE_PAM 116674874Smarkm if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 116774874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 116874874Smarkm if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 116974874Smarkm syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 117074874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 117174874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 117274874Smarkm pamh = NULL; 117374874Smarkm#endif 11741592Srgrimes logged_in = 0; 11751592Srgrimes guest = 0; 117617435Spst dochroot = 0; 11771592Srgrimes} 11781592Srgrimes 117974874Smarkm#ifdef USE_PAM 118051433Smarkm 118151433Smarkm/* 118251433Smarkm * the following code is stolen from imap-uw PAM authentication module and 118351433Smarkm * login.c 118451433Smarkm */ 118551433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL) 118651433Smarkm 118751433Smarkmstruct cred_t { 118851433Smarkm const char *uname; /* user name */ 118951433Smarkm const char *pass; /* password */ 119051433Smarkm}; 119151433Smarkmtypedef struct cred_t cred_t; 119251433Smarkm 119351433Smarkmstatic int 119451433Smarkmauth_conv(int num_msg, const struct pam_message **msg, 119551433Smarkm struct pam_response **resp, void *appdata) 119651433Smarkm{ 119751433Smarkm int i; 119851433Smarkm cred_t *cred = (cred_t *) appdata; 119991244Sdes struct pam_response *reply; 120051433Smarkm 120191244Sdes reply = calloc(num_msg, sizeof *reply); 120291244Sdes if (reply == NULL) 120391244Sdes return PAM_BUF_ERR; 120491244Sdes 120551433Smarkm for (i = 0; i < num_msg; i++) { 120651433Smarkm switch (msg[i]->msg_style) { 120751433Smarkm case PAM_PROMPT_ECHO_ON: /* assume want user name */ 120851433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 120951433Smarkm reply[i].resp = COPY_STRING(cred->uname); 121051433Smarkm /* PAM frees resp. */ 121151433Smarkm break; 121251433Smarkm case PAM_PROMPT_ECHO_OFF: /* assume want password */ 121351433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 121451433Smarkm reply[i].resp = COPY_STRING(cred->pass); 121551433Smarkm /* PAM frees resp. */ 121651433Smarkm break; 121751433Smarkm case PAM_TEXT_INFO: 121851433Smarkm case PAM_ERROR_MSG: 121951433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 122051433Smarkm reply[i].resp = NULL; 122151433Smarkm break; 122251433Smarkm default: /* unknown message style */ 122351433Smarkm free(reply); 122451433Smarkm return PAM_CONV_ERR; 122551433Smarkm } 122651433Smarkm } 122751433Smarkm 122851433Smarkm *resp = reply; 122951433Smarkm return PAM_SUCCESS; 123051433Smarkm} 123151433Smarkm 123251433Smarkm/* 123351433Smarkm * Attempt to authenticate the user using PAM. Returns 0 if the user is 123451433Smarkm * authenticated, or 1 if not authenticated. If some sort of PAM system 123551433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 123651433Smarkm * function returns -1. This can be used as an indication that we should 123751433Smarkm * fall back to a different authentication mechanism. 123851433Smarkm */ 123951433Smarkmstatic int 124051433Smarkmauth_pam(struct passwd **ppw, const char *pass) 124151433Smarkm{ 124251433Smarkm pam_handle_t *pamh = NULL; 124351433Smarkm const char *tmpl_user; 124451433Smarkm const void *item; 124551433Smarkm int rval; 124651433Smarkm int e; 124751433Smarkm cred_t auth_cred = { (*ppw)->pw_name, pass }; 124851433Smarkm struct pam_conv conv = { &auth_conv, &auth_cred }; 124951433Smarkm 125051433Smarkm e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 125151433Smarkm if (e != PAM_SUCCESS) { 125251433Smarkm syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); 125351433Smarkm return -1; 125451433Smarkm } 125551433Smarkm 125667007Sguido e = pam_set_item(pamh, PAM_RHOST, remotehost); 125767007Sguido if (e != PAM_SUCCESS) { 125867007Sguido syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 125967007Sguido pam_strerror(pamh, e)); 126067007Sguido return -1; 126167007Sguido } 126267007Sguido 126351433Smarkm e = pam_authenticate(pamh, 0); 126451433Smarkm switch (e) { 126551433Smarkm case PAM_SUCCESS: 126651433Smarkm /* 126751433Smarkm * With PAM we support the concept of a "template" 126851433Smarkm * user. The user enters a login name which is 126951433Smarkm * authenticated by PAM, usually via a remote service 127051433Smarkm * such as RADIUS or TACACS+. If authentication 127151433Smarkm * succeeds, a different but related "template" name 127251433Smarkm * is used for setting the credentials, shell, and 127351433Smarkm * home directory. The name the user enters need only 127451433Smarkm * exist on the remote authentication server, but the 127551433Smarkm * template name must be present in the local password 127651433Smarkm * database. 127751433Smarkm * 127851433Smarkm * This is supported by two various mechanisms in the 127951433Smarkm * individual modules. However, from the application's 128051433Smarkm * point of view, the template user is always passed 128151433Smarkm * back as a changed value of the PAM_USER item. 128251433Smarkm */ 128351433Smarkm if ((e = pam_get_item(pamh, PAM_USER, &item)) == 128451433Smarkm PAM_SUCCESS) { 128551433Smarkm tmpl_user = (const char *) item; 128651433Smarkm if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 128751433Smarkm *ppw = getpwnam(tmpl_user); 128851433Smarkm } else 128951433Smarkm syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 129051433Smarkm pam_strerror(pamh, e)); 129151433Smarkm rval = 0; 129251433Smarkm break; 129351433Smarkm 129451433Smarkm case PAM_AUTH_ERR: 129551433Smarkm case PAM_USER_UNKNOWN: 129651433Smarkm case PAM_MAXTRIES: 129751433Smarkm rval = 1; 129851433Smarkm break; 129951433Smarkm 130051433Smarkm default: 130174874Smarkm syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 130251433Smarkm rval = -1; 130351433Smarkm break; 130451433Smarkm } 130551433Smarkm 130674874Smarkm if (rval == 0) { 130774874Smarkm e = pam_acct_mgmt(pamh, 0); 130874874Smarkm if (e == PAM_NEW_AUTHTOK_REQD) { 130974874Smarkm e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 131074874Smarkm if (e != PAM_SUCCESS) { 131174874Smarkm syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e)); 131274874Smarkm rval = 1; 131374874Smarkm } 131474874Smarkm } else if (e != PAM_SUCCESS) { 131574874Smarkm rval = 1; 131674874Smarkm } 131751433Smarkm } 131874874Smarkm 131974874Smarkm if (rval != 0) { 132074874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 132174874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 132274874Smarkm } 132374874Smarkm pamh = NULL; 132474874Smarkm } 132551433Smarkm return rval; 132651433Smarkm} 132751433Smarkm 132874874Smarkm#endif /* USE_PAM */ 132951433Smarkm 13301592Srgrimesvoid 133190148Simppass(char *passwd) 13321592Srgrimes{ 133317435Spst int rval; 13341592Srgrimes FILE *fd; 133525101Sdavidn#ifdef LOGIN_CAP 133625101Sdavidn login_cap_t *lc = NULL; 133725101Sdavidn#endif 133874874Smarkm#ifdef USE_PAM 133974874Smarkm int e; 134074874Smarkm#endif 1341110036Syar char *chrootdir; 1342109939Syar char *residue = NULL; 134388763Sache char *xpasswd; 13441592Srgrimes 13451592Srgrimes if (logged_in || askpasswd == 0) { 13461592Srgrimes reply(503, "Login with USER first."); 13471592Srgrimes return; 13481592Srgrimes } 13491592Srgrimes askpasswd = 0; 13501592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 135117435Spst if (pw == NULL) { 135217435Spst rval = 1; /* failure below */ 135317435Spst goto skip; 135417435Spst } 135574874Smarkm#ifdef USE_PAM 135651433Smarkm rval = auth_pam(&pw, passwd); 135789622Sache if (rval >= 0) { 135889622Sache opieunlock(); 135917435Spst goto skip; 136089622Sache } 136189622Sache#endif 136288763Sache if (opieverify(&opiedata, passwd) == 0) 136388763Sache xpasswd = pw->pw_passwd; 136489622Sache else if (pwok) { 136588763Sache xpasswd = crypt(passwd, pw->pw_passwd); 136689622Sache if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 136789622Sache xpasswd = ":"; 136889622Sache } else { 136988763Sache rval = 1; 137088763Sache goto skip; 137188763Sache } 137288763Sache rval = strcmp(pw->pw_passwd, xpasswd); 137389622Sache if (pw->pw_expire && time(NULL) >= pw->pw_expire) 137417435Spst rval = 1; /* failure */ 137517435Spstskip: 137617435Spst /* 137717435Spst * If rval == 1, the user failed the authentication check 137851433Smarkm * above. If rval == 0, either PAM or local authentication 137917435Spst * succeeded. 138017435Spst */ 138117435Spst if (rval) { 13821592Srgrimes reply(530, "Login incorrect."); 1383110691Syar if (logging) { 13841592Srgrimes syslog(LOG_NOTICE, 1385110691Syar "FTP LOGIN FAILED FROM %s", 1386110691Syar remotehost); 1387110691Syar syslog(LOG_AUTHPRIV | LOG_NOTICE, 13881592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 13891592Srgrimes remotehost, curname); 1390110691Syar } 13911592Srgrimes pw = NULL; 13921592Srgrimes if (login_attempts++ >= 5) { 13931592Srgrimes syslog(LOG_NOTICE, 13941592Srgrimes "repeated login failures from %s", 13951592Srgrimes remotehost); 13961592Srgrimes exit(0); 13971592Srgrimes } 13981592Srgrimes return; 13991592Srgrimes } 14001592Srgrimes } 14011592Srgrimes login_attempts = 0; /* this time successful */ 14021592Srgrimes if (setegid((gid_t)pw->pw_gid) < 0) { 14031592Srgrimes reply(550, "Can't set gid."); 14041592Srgrimes return; 14051592Srgrimes } 140625101Sdavidn /* May be overridden by login.conf */ 140725101Sdavidn (void) umask(defumask); 140825101Sdavidn#ifdef LOGIN_CAP 140925674Sdavidn if ((lc = login_getpwclass(pw)) != NULL) { 141025101Sdavidn char remote_ip[MAXHOSTNAMELEN]; 141125101Sdavidn 141256668Sshin getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 141356668Sshin remote_ip, sizeof(remote_ip) - 1, NULL, 0, 141499255Sume NI_NUMERICHOST); 141525101Sdavidn remote_ip[sizeof(remote_ip) - 1] = 0; 141625101Sdavidn if (!auth_hostok(lc, remotehost, remote_ip)) { 141725101Sdavidn syslog(LOG_INFO|LOG_AUTH, 141825101Sdavidn "FTP LOGIN FAILED (HOST) as %s: permission denied.", 141925101Sdavidn pw->pw_name); 142025101Sdavidn reply(530, "Permission denied.\n"); 142125101Sdavidn pw = NULL; 142225101Sdavidn return; 142325101Sdavidn } 142425101Sdavidn if (!auth_timeok(lc, time(NULL))) { 142525101Sdavidn reply(530, "Login not available right now.\n"); 142625101Sdavidn pw = NULL; 142725101Sdavidn return; 142825101Sdavidn } 142925101Sdavidn } 143025101Sdavidn setusercontext(lc, pw, (uid_t)0, 143140310Sdes LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| 1432105877Srwatson LOGIN_SETRESOURCES|LOGIN_SETUMASK|LOGIN_SETMAC); 143325101Sdavidn#else 143440310Sdes setlogin(pw->pw_name); 14351592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 143625101Sdavidn#endif 14371592Srgrimes 143874874Smarkm#ifdef USE_PAM 143974874Smarkm if (pamh) { 144074874Smarkm if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 144174874Smarkm syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 144274874Smarkm } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 144374874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 144474874Smarkm } 144574874Smarkm } 144674874Smarkm#endif 144774874Smarkm 14481592Srgrimes /* open wtmp before chroot */ 1449102311Syar if (dowtmp) 1450102311Syar ftpd_logwtmp(ttyline, pw->pw_name, 1451102311Syar (struct sockaddr *)&his_addr); 14521592Srgrimes logged_in = 1; 14531592Srgrimes 145417435Spst if (guest && stats && statfd < 0) 145525283Sdavidn#ifdef VIRTUAL_HOSTING 145625283Sdavidn if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) 145725283Sdavidn#else 14586740Sguido if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) 145925283Sdavidn#endif 14606740Sguido stats = 0; 14616740Sguido 146225101Sdavidn dochroot = 1463109939Syar checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue) 146425101Sdavidn#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 1465109893Syar || login_getcapbool(lc, "ftp-chroot", 0) 146625101Sdavidn#endif 1467109893Syar ; 1468110036Syar chrootdir = NULL; 1469110036Syar /* 1470110036Syar * For a chrooted local user, 1471110036Syar * a) see whether ftpchroot(5) specifies a chroot directory, 1472110036Syar * b) extract the directory pathname from the line, 1473110036Syar * c) expand it to the absolute pathname if necessary. 1474110036Syar */ 1475110036Syar if (dochroot && residue && 1476117349Syar (chrootdir = strtok(residue, " \t")) != NULL) { 1477117349Syar if (chrootdir[0] != '/') 1478117349Syar asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); 1479117349Syar else 1480117349Syar chrootdir = strdup(chrootdir); /* so it can be freed */ 1481110036Syar if (chrootdir == NULL) 1482110036Syar fatalerror("Ran out of memory."); 1483110036Syar } 1484110036Syar if (guest || dochroot) { 14851592Srgrimes /* 1486110036Syar * If no chroot directory set yet, use the login directory. 1487110036Syar * Copy it so it can be modified while pw->pw_dir stays intact. 14881592Srgrimes */ 1489110036Syar if (chrootdir == NULL && 1490110036Syar (chrootdir = strdup(pw->pw_dir)) == NULL) 1491110036Syar fatalerror("Ran out of memory."); 1492110036Syar /* 1493110036Syar * Check for the "/chroot/./home" syntax, 1494110036Syar * separate the chroot and home directory pathnames. 1495110036Syar */ 1496110036Syar if ((homedir = strstr(chrootdir, "/./")) != NULL) { 1497110036Syar *(homedir++) = '\0'; /* wipe '/' */ 1498110036Syar homedir++; /* skip '.' */ 1499110036Syar /* so chrootdir can be freed later */ 1500110036Syar if ((homedir = strdup(homedir)) == NULL) 1501109893Syar fatalerror("Ran out of memory."); 1502110036Syar } else { 1503110036Syar /* 1504110036Syar * We MUST do a chdir() after the chroot. Otherwise 1505110036Syar * the old current directory will be accessible as "." 1506110036Syar * outside the new root! 1507110036Syar */ 1508110036Syar homedir = "/"; 1509109939Syar } 1510110036Syar /* 1511110036Syar * Finally, do chroot() 1512110036Syar */ 1513110036Syar if (chroot(chrootdir) < 0) { 151417435Spst reply(550, "Can't change root."); 151517435Spst goto bad; 151617435Spst } 1517110036Syar } else /* real user w/o chroot */ 1518110036Syar homedir = pw->pw_dir; 1519110036Syar /* 1520110036Syar * Set euid *before* doing chdir() so 1521110036Syar * a) the user won't be carried to a directory that he couldn't reach 1522110036Syar * on his own due to no permission to upper path components, 1523110036Syar * b) NFS mounted homedirs w/restrictive permissions will be accessible 1524110036Syar * (uid 0 has no root power over NFS if not mapped explicitly.) 1525110036Syar */ 15261592Srgrimes if (seteuid((uid_t)pw->pw_uid) < 0) { 15271592Srgrimes reply(550, "Can't set uid."); 15281592Srgrimes goto bad; 15291592Srgrimes } 1530110036Syar if (chdir(homedir) < 0) { 1531110036Syar if (guest || dochroot) { 1532110036Syar reply(550, "Can't change to base directory."); 1533110036Syar goto bad; 1534110036Syar } else { 1535110036Syar if (chdir("/") < 0) { 1536110036Syar reply(550, "Root is inaccessible."); 1537110036Syar goto bad; 1538110036Syar } 1539110036Syar lreply(230, "No directory! Logging in with home=/"); 1540110036Syar } 1541110036Syar } 15428696Sdg 15431592Srgrimes /* 15441592Srgrimes * Display a login message, if it exists. 15451592Srgrimes * N.B. reply(230,) must follow the message. 15461592Srgrimes */ 154725283Sdavidn#ifdef VIRTUAL_HOSTING 154825283Sdavidn if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { 154925283Sdavidn#else 15501592Srgrimes if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 155125283Sdavidn#endif 15521592Srgrimes char *cp, line[LINE_MAX]; 15531592Srgrimes 15541592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 15551592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 15561592Srgrimes *cp = '\0'; 15571592Srgrimes lreply(230, "%s", line); 15581592Srgrimes } 15591592Srgrimes (void) fflush(stdout); 15601592Srgrimes (void) fclose(fd); 15611592Srgrimes } 15621592Srgrimes if (guest) { 15636740Sguido if (ident != NULL) 15646740Sguido free(ident); 156517433Spst ident = strdup(passwd); 156617433Spst if (ident == NULL) 156776096Smarkm fatalerror("Ran out of memory."); 156817433Spst 15691592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 15701592Srgrimes#ifdef SETPROCTITLE 157125283Sdavidn#ifdef VIRTUAL_HOSTING 157225283Sdavidn if (thishost != firsthost) 157325283Sdavidn snprintf(proctitle, sizeof(proctitle), 157483308Smikeh "%s: anonymous(%s)/%s", remotehost, hostname, 157583308Smikeh passwd); 157625283Sdavidn else 157725283Sdavidn#endif 157825283Sdavidn snprintf(proctitle, sizeof(proctitle), 157983308Smikeh "%s: anonymous/%s", remotehost, passwd); 158013139Speter setproctitle("%s", proctitle); 15811592Srgrimes#endif /* SETPROCTITLE */ 15821592Srgrimes if (logging) 15831592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 15841592Srgrimes remotehost, passwd); 15851592Srgrimes } else { 158684841Syar if (dochroot) 158784841Syar reply(230, "User %s logged in, " 158884841Syar "access restrictions apply.", pw->pw_name); 158984841Syar else 159084841Syar reply(230, "User %s logged in.", pw->pw_name); 159125986Sdanny 15921592Srgrimes#ifdef SETPROCTITLE 15931592Srgrimes snprintf(proctitle, sizeof(proctitle), 159484842Syar "%s: user/%s", remotehost, pw->pw_name); 159513139Speter setproctitle("%s", proctitle); 15961592Srgrimes#endif /* SETPROCTITLE */ 15971592Srgrimes if (logging) 15981592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 15991592Srgrimes remotehost, pw->pw_name); 16001592Srgrimes } 160125101Sdavidn#ifdef LOGIN_CAP 160225101Sdavidn login_close(lc); 160325101Sdavidn#endif 1604110036Syar if (chrootdir) 1605110036Syar free(chrootdir); 1606110036Syar if (residue) 1607110036Syar free(residue); 16081592Srgrimes return; 16091592Srgrimesbad: 16101592Srgrimes /* Forget all about it... */ 161125101Sdavidn#ifdef LOGIN_CAP 161225101Sdavidn login_close(lc); 161325101Sdavidn#endif 1614110036Syar if (chrootdir) 1615110036Syar free(chrootdir); 1616110036Syar if (residue) 1617110036Syar free(residue); 16181592Srgrimes end_login(); 16191592Srgrimes} 16201592Srgrimes 16211592Srgrimesvoid 162290148Simpretrieve(char *cmd, char *name) 16231592Srgrimes{ 16241592Srgrimes FILE *fin, *dout; 16251592Srgrimes struct stat st; 162690148Simp int (*closefunc)(FILE *); 162736612Sjb time_t start; 16281592Srgrimes 16291592Srgrimes if (cmd == 0) { 16301592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 16311592Srgrimes st.st_size = 0; 16321592Srgrimes } else { 16331592Srgrimes char line[BUFSIZ]; 16341592Srgrimes 163531973Simp (void) snprintf(line, sizeof(line), cmd, name), name = line; 16361592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 16371592Srgrimes st.st_size = -1; 16381592Srgrimes st.st_blksize = BUFSIZ; 16391592Srgrimes } 16401592Srgrimes if (fin == NULL) { 16411592Srgrimes if (errno != 0) { 16421592Srgrimes perror_reply(550, name); 16431592Srgrimes if (cmd == 0) { 16441592Srgrimes LOGCMD("get", name); 16451592Srgrimes } 16461592Srgrimes } 16471592Srgrimes return; 16481592Srgrimes } 16491592Srgrimes byte_count = -1; 1650110144Syar if (cmd == 0) { 1651110144Syar if (fstat(fileno(fin), &st) < 0) { 1652110144Syar perror_reply(550, name); 1653110144Syar goto done; 1654110144Syar } 1655110144Syar if (!S_ISREG(st.st_mode)) { 1656125565Syar /* 1657125565Syar * Never sending a raw directory is a workaround 1658125565Syar * for buggy clients that will attempt to RETR 1659125565Syar * a directory before listing it, e.g., Mozilla. 1660125565Syar * Preventing a guest from getting irregular files 1661125565Syar * is a simple security measure. 1662125565Syar */ 1663125565Syar if (S_ISDIR(st.st_mode) || guest) { 1664110144Syar reply(550, "%s: not a plain file.", name); 1665110144Syar goto done; 1666110144Syar } 1667110144Syar st.st_size = -1; 1668110144Syar /* st.st_blksize is set for all descriptor types */ 1669110144Syar } 16701592Srgrimes } 16711592Srgrimes if (restart_point) { 16721592Srgrimes if (type == TYPE_A) { 16731592Srgrimes off_t i, n; 16741592Srgrimes int c; 16751592Srgrimes 16761592Srgrimes n = restart_point; 16771592Srgrimes i = 0; 16781592Srgrimes while (i++ < n) { 16791592Srgrimes if ((c=getc(fin)) == EOF) { 16801592Srgrimes perror_reply(550, name); 16811592Srgrimes goto done; 16821592Srgrimes } 16831592Srgrimes if (c == '\n') 16841592Srgrimes i++; 16851592Srgrimes } 16861592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 16871592Srgrimes perror_reply(550, name); 16881592Srgrimes goto done; 16891592Srgrimes } 16901592Srgrimes } 16911592Srgrimes dout = dataconn(name, st.st_size, "w"); 16921592Srgrimes if (dout == NULL) 16931592Srgrimes goto done; 16946740Sguido time(&start); 16958240Swollman send_data(fin, dout, st.st_blksize, st.st_size, 16968240Swollman restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 16976740Sguido if (cmd == 0 && guest && stats) 169817433Spst logxfer(name, st.st_size, start); 16991592Srgrimes (void) fclose(dout); 17001592Srgrimes data = -1; 17011592Srgrimes pdata = -1; 17021592Srgrimesdone: 17031592Srgrimes if (cmd == 0) 17041592Srgrimes LOGBYTES("get", name, byte_count); 17051592Srgrimes (*closefunc)(fin); 17061592Srgrimes} 17071592Srgrimes 17081592Srgrimesvoid 170990148Simpstore(char *name, char *mode, int unique) 17101592Srgrimes{ 1711101537Syar int fd; 17121592Srgrimes FILE *fout, *din; 171390148Simp int (*closefunc)(FILE *); 17141592Srgrimes 1715101537Syar if (*mode == 'a') { /* APPE */ 1716101537Syar if (unique) { 1717101537Syar /* Programming error */ 1718101537Syar syslog(LOG_ERR, "Internal: unique flag to APPE"); 1719101537Syar unique = 0; 1720101537Syar } 1721101537Syar if (guest && noguestmod) { 1722101537Syar reply(550, "Appending to existing file denied"); 1723101537Syar goto err; 1724101537Syar } 1725101537Syar restart_point = 0; /* not affected by preceding REST */ 17261592Srgrimes } 1727101537Syar if (unique) /* STOU overrides REST */ 1728101537Syar restart_point = 0; 1729101537Syar if (guest && noguestmod) { 1730101537Syar if (restart_point) { /* guest STOR w/REST */ 1731101537Syar reply(550, "Modifying existing file denied"); 1732101537Syar goto err; 1733101537Syar } else /* treat guest STOR as STOU */ 1734101537Syar unique = 1; 1735101537Syar } 17361592Srgrimes 17371592Srgrimes if (restart_point) 1738101537Syar mode = "r+"; /* so ASCII manual seek can work */ 1739101537Syar if (unique) { 1740101537Syar if ((fd = guniquefd(name, &name)) < 0) 1741101537Syar goto err; 1742101537Syar fout = fdopen(fd, mode); 1743101537Syar } else 1744101537Syar fout = fopen(name, mode); 17451592Srgrimes closefunc = fclose; 17461592Srgrimes if (fout == NULL) { 17471592Srgrimes perror_reply(553, name); 1748101537Syar goto err; 17491592Srgrimes } 17501592Srgrimes byte_count = -1; 17511592Srgrimes if (restart_point) { 17521592Srgrimes if (type == TYPE_A) { 17531592Srgrimes off_t i, n; 17541592Srgrimes int c; 17551592Srgrimes 17561592Srgrimes n = restart_point; 17571592Srgrimes i = 0; 17581592Srgrimes while (i++ < n) { 17591592Srgrimes if ((c=getc(fout)) == EOF) { 17601592Srgrimes perror_reply(550, name); 17611592Srgrimes goto done; 17621592Srgrimes } 17631592Srgrimes if (c == '\n') 17641592Srgrimes i++; 17651592Srgrimes } 17661592Srgrimes /* 17671592Srgrimes * We must do this seek to "current" position 17681592Srgrimes * because we are changing from reading to 17691592Srgrimes * writing. 17701592Srgrimes */ 177182792Sache if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { 17721592Srgrimes perror_reply(550, name); 17731592Srgrimes goto done; 17741592Srgrimes } 17751592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 17761592Srgrimes perror_reply(550, name); 17771592Srgrimes goto done; 17781592Srgrimes } 17791592Srgrimes } 17801592Srgrimes din = dataconn(name, (off_t)-1, "r"); 17811592Srgrimes if (din == NULL) 17821592Srgrimes goto done; 17831592Srgrimes if (receive_data(din, fout) == 0) { 17841592Srgrimes if (unique) 17851592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 17861592Srgrimes name); 17871592Srgrimes else 17881592Srgrimes reply(226, "Transfer complete."); 17891592Srgrimes } 17901592Srgrimes (void) fclose(din); 17911592Srgrimes data = -1; 17921592Srgrimes pdata = -1; 17931592Srgrimesdone: 1794102566Syar LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); 17951592Srgrimes (*closefunc)(fout); 1796101537Syar return; 1797101537Syarerr: 1798101537Syar LOGCMD(*mode == 'a' ? "append" : "put" , name); 1799101537Syar return; 18001592Srgrimes} 18011592Srgrimes 18021592Srgrimesstatic FILE * 180390148Simpgetdatasock(char *mode) 18041592Srgrimes{ 18051592Srgrimes int on = 1, s, t, tries; 18061592Srgrimes 18071592Srgrimes if (data >= 0) 18081592Srgrimes return (fdopen(data, mode)); 18091592Srgrimes (void) seteuid((uid_t)0); 181056668Sshin 181156668Sshin s = socket(data_dest.su_family, SOCK_STREAM, 0); 18121592Srgrimes if (s < 0) 18131592Srgrimes goto bad; 1814100612Syar if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1815100609Syar syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 18161592Srgrimes /* anchor socket to avoid multi-homing problems */ 181756668Sshin data_source = ctrl_addr; 1818109742Syar data_source.su_port = htons(dataport); 18191592Srgrimes for (tries = 1; ; tries++) { 18201592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 182156668Sshin data_source.su_len) >= 0) 18221592Srgrimes break; 18231592Srgrimes if (errno != EADDRINUSE || tries > 10) 18241592Srgrimes goto bad; 18251592Srgrimes sleep(tries); 18261592Srgrimes } 18271592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 18281592Srgrimes#ifdef IP_TOS 182956668Sshin if (data_source.su_family == AF_INET) 183056668Sshin { 18311592Srgrimes on = IPTOS_THROUGHPUT; 1832100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 1833100609Syar syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 183456668Sshin } 18351592Srgrimes#endif 18368240Swollman#ifdef TCP_NOPUSH 18378240Swollman /* 18388240Swollman * Turn off push flag to keep sender TCP from sending short packets 18398240Swollman * at the boundaries of each write(). Should probably do a SO_SNDBUF 18408240Swollman * to set the send buffer size as well, but that may not be desirable 18418240Swollman * in heavy-load situations. 18428240Swollman */ 18438240Swollman on = 1; 1844100612Syar if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 1845100609Syar syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 18468240Swollman#endif 18478240Swollman#ifdef SO_SNDBUF 18488240Swollman on = 65536; 1849100612Syar if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof on) < 0) 1850100609Syar syslog(LOG_WARNING, "data setsockopt (SO_SNDBUF): %m"); 18518240Swollman#endif 18528240Swollman 18531592Srgrimes return (fdopen(s, mode)); 18541592Srgrimesbad: 18551592Srgrimes /* Return the real value of errno (close may change it) */ 18561592Srgrimes t = errno; 18571592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 18581592Srgrimes (void) close(s); 18591592Srgrimes errno = t; 18601592Srgrimes return (NULL); 18611592Srgrimes} 18621592Srgrimes 18631592Srgrimesstatic FILE * 186490148Simpdataconn(char *name, off_t size, char *mode) 18651592Srgrimes{ 18661592Srgrimes char sizebuf[32]; 18671592Srgrimes FILE *file; 1868109611Scjc int retry = 0, tos, conerrno; 18691592Srgrimes 18701592Srgrimes file_size = size; 18711592Srgrimes byte_count = 0; 18721592Srgrimes if (size != (off_t) -1) 187331973Simp (void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size); 18741592Srgrimes else 187531973Simp *sizebuf = '\0'; 18761592Srgrimes if (pdata >= 0) { 187756668Sshin union sockunion from; 187886628Syar int flags; 187956668Sshin int s, fromlen = ctrl_addr.su_len; 188012532Sguido struct timeval timeout; 188112532Sguido fd_set set; 18821592Srgrimes 188312532Sguido FD_ZERO(&set); 188412532Sguido FD_SET(pdata, &set); 188512532Sguido 188612532Sguido timeout.tv_usec = 0; 188712532Sguido timeout.tv_sec = 120; 188812532Sguido 188986628Syar /* 189086628Syar * Granted a socket is in the blocking I/O mode, 189186628Syar * accept() will block after a successful select() 189286628Syar * if the selected connection dies in between. 189386628Syar * Therefore set the non-blocking I/O flag here. 189486628Syar */ 189586628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 189686628Syar fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 189786628Syar goto pdata_err; 189886628Syar if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) <= 0 || 189986628Syar (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 190086628Syar goto pdata_err; 19011592Srgrimes (void) close(pdata); 19021592Srgrimes pdata = s; 190386628Syar /* 1904101809Syar * Unset the inherited non-blocking I/O flag 1905101809Syar * on the child socket so stdio can work on it. 190686628Syar */ 190786628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 190886628Syar fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 190986628Syar goto pdata_err; 19101592Srgrimes#ifdef IP_TOS 191156668Sshin if (from.su_family == AF_INET) 191256668Sshin { 191317435Spst tos = IPTOS_THROUGHPUT; 1914100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 1915100609Syar syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 191656668Sshin } 19171592Srgrimes#endif 19181592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19191592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19201592Srgrimes return (fdopen(pdata, mode)); 192186628Syarpdata_err: 192286628Syar reply(425, "Can't open data connection."); 192386628Syar (void) close(pdata); 192486628Syar pdata = -1; 192586628Syar return (NULL); 19261592Srgrimes } 19271592Srgrimes if (data >= 0) { 19281592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 19291592Srgrimes name, sizebuf); 19301592Srgrimes usedefault = 1; 19311592Srgrimes return (fdopen(data, mode)); 19321592Srgrimes } 19331592Srgrimes if (usedefault) 19341592Srgrimes data_dest = his_addr; 19351592Srgrimes usedefault = 1; 1936109611Scjc do { 1937109611Scjc file = getdatasock(mode); 1938109611Scjc if (file == NULL) { 1939109611Scjc char hostbuf[BUFSIZ], portbuf[BUFSIZ]; 1940109611Scjc getnameinfo((struct sockaddr *)&data_source, 1941109611Scjc data_source.su_len, hostbuf, sizeof(hostbuf) - 1, 1942109611Scjc portbuf, sizeof(portbuf), 1943109611Scjc NI_NUMERICHOST|NI_NUMERICSERV); 1944109611Scjc reply(425, "Can't create data socket (%s,%s): %s.", 1945109611Scjc hostbuf, portbuf, strerror(errno)); 1946109611Scjc return (NULL); 1947109611Scjc } 1948109611Scjc data = fileno(file); 1949109611Scjc conerrno = 0; 1950109611Scjc if (connect(data, (struct sockaddr *)&data_dest, 1951109611Scjc data_dest.su_len) == 0) 1952109611Scjc break; 1953109611Scjc conerrno = errno; 1954109611Scjc (void) fclose(file); 1955109611Scjc data = -1; 1956109611Scjc if (conerrno == EADDRINUSE) { 19571592Srgrimes sleep((unsigned) swaitint); 19581592Srgrimes retry += swaitint; 1959109611Scjc } else { 1960109611Scjc break; 19611592Srgrimes } 1962109611Scjc } while (retry <= swaitmax); 1963109611Scjc if (conerrno != 0) { 19641592Srgrimes perror_reply(425, "Can't build data connection"); 19651592Srgrimes return (NULL); 19661592Srgrimes } 19671592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19681592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19691592Srgrimes return (file); 19701592Srgrimes} 19711592Srgrimes 19721592Srgrimes/* 19731592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate 19748240Swollman * encapsulation of the data subject to Mode, Structure, and Type. 19751592Srgrimes * 19761592Srgrimes * NB: Form isn't handled. 19771592Srgrimes */ 197889935Syarstatic int 197990148Simpsend_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg) 19801592Srgrimes{ 1981122751Syar int c, cp, filefd, netfd; 198270205Sdan char *buf; 198370205Sdan off_t cnt; 19841592Srgrimes 19851592Srgrimes transflag++; 19861592Srgrimes switch (type) { 19871592Srgrimes 19881592Srgrimes case TYPE_A: 1989122751Syar cp = '\0'; 19901592Srgrimes while ((c = getc(instr)) != EOF) { 199189935Syar if (recvurg) 199289935Syar goto got_oob; 19931592Srgrimes byte_count++; 1994122751Syar if (c == '\n' && cp != '\r') { 19951592Srgrimes if (ferror(outstr)) 19961592Srgrimes goto data_err; 19971592Srgrimes (void) putc('\r', outstr); 19981592Srgrimes } 19991592Srgrimes (void) putc(c, outstr); 2000122751Syar cp = c; 20011592Srgrimes } 200289935Syar if (recvurg) 200389935Syar goto got_oob; 20041592Srgrimes fflush(outstr); 20051592Srgrimes transflag = 0; 20061592Srgrimes if (ferror(instr)) 20071592Srgrimes goto file_err; 20081592Srgrimes if (ferror(outstr)) 20091592Srgrimes goto data_err; 20101592Srgrimes reply(226, "Transfer complete."); 201189935Syar return (0); 20121592Srgrimes 20131592Srgrimes case TYPE_I: 20141592Srgrimes case TYPE_L: 20158240Swollman /* 20168240Swollman * isreg is only set if we are not doing restart and we 20178240Swollman * are sending a regular file 20188240Swollman */ 20198240Swollman netfd = fileno(outstr); 20208870Srgrimes filefd = fileno(instr); 20218240Swollman 202270205Sdan if (isreg) { 202370205Sdan 202470205Sdan off_t offset; 202570205Sdan int err; 202670205Sdan 202770205Sdan err = cnt = offset = 0; 202870205Sdan 202990604Smaxim while (err != -1 && filesize > 0) { 203090604Smaxim err = sendfile(filefd, netfd, offset, 0, 203170205Sdan (struct sf_hdtr *) NULL, &cnt, 0); 203299212Smaxim /* 203399212Smaxim * Calculate byte_count before OOB processing. 203499212Smaxim * It can be used in myoob() later. 203599212Smaxim */ 203699212Smaxim byte_count += cnt; 203789935Syar if (recvurg) 203889935Syar goto got_oob; 203970205Sdan offset += cnt; 204090604Smaxim filesize -= cnt; 20418240Swollman 204270205Sdan if (err == -1) { 204370205Sdan if (!cnt) 204470205Sdan goto oldway; 204570205Sdan 204670205Sdan goto data_err; 204770205Sdan } 204870205Sdan } 204970205Sdan 205099318Sdan transflag = 0; 20518240Swollman reply(226, "Transfer complete."); 205289935Syar return (0); 20538240Swollman } 20548240Swollman 20558240Swollmanoldway: 20561592Srgrimes if ((buf = malloc((u_int)blksize)) == NULL) { 20571592Srgrimes transflag = 0; 20581592Srgrimes perror_reply(451, "Local resource failure: malloc"); 205989935Syar return (-1); 20601592Srgrimes } 20618870Srgrimes 20621592Srgrimes while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 20631592Srgrimes write(netfd, buf, cnt) == cnt) 20641592Srgrimes byte_count += cnt; 20651592Srgrimes transflag = 0; 20661592Srgrimes (void)free(buf); 20671592Srgrimes if (cnt != 0) { 20681592Srgrimes if (cnt < 0) 20691592Srgrimes goto file_err; 20701592Srgrimes goto data_err; 20711592Srgrimes } 20721592Srgrimes reply(226, "Transfer complete."); 207389935Syar return (0); 20741592Srgrimes default: 20751592Srgrimes transflag = 0; 20761592Srgrimes reply(550, "Unimplemented TYPE %d in send_data", type); 207789935Syar return (-1); 20781592Srgrimes } 20791592Srgrimes 20801592Srgrimesdata_err: 20811592Srgrimes transflag = 0; 20821592Srgrimes perror_reply(426, "Data connection"); 208389935Syar return (-1); 20841592Srgrimes 20851592Srgrimesfile_err: 20861592Srgrimes transflag = 0; 20871592Srgrimes perror_reply(551, "Error on input file"); 208889935Syar return (-1); 208989935Syar 209089935Syargot_oob: 209189935Syar myoob(); 209289935Syar recvurg = 0; 209389935Syar transflag = 0; 209489935Syar return (-1); 20951592Srgrimes} 20961592Srgrimes 20971592Srgrimes/* 20981592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 20991592Srgrimes * the data subject to Mode, Structure, and Type. 21001592Srgrimes * 21011592Srgrimes * N.B.: Form isn't handled. 21021592Srgrimes */ 21031592Srgrimesstatic int 210490148Simpreceive_data(FILE *instr, FILE *outstr) 21051592Srgrimes{ 21061592Srgrimes int c; 210717433Spst int cnt, bare_lfs; 21081592Srgrimes char buf[BUFSIZ]; 21091592Srgrimes 21101592Srgrimes transflag++; 211117433Spst bare_lfs = 0; 211217433Spst 21131592Srgrimes switch (type) { 21141592Srgrimes 21151592Srgrimes case TYPE_I: 21161592Srgrimes case TYPE_L: 21171592Srgrimes while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 211889935Syar if (recvurg) 211989935Syar goto got_oob; 21201592Srgrimes if (write(fileno(outstr), buf, cnt) != cnt) 21211592Srgrimes goto file_err; 21221592Srgrimes byte_count += cnt; 21231592Srgrimes } 212489935Syar if (recvurg) 212589935Syar goto got_oob; 21261592Srgrimes if (cnt < 0) 21271592Srgrimes goto data_err; 21281592Srgrimes transflag = 0; 21291592Srgrimes return (0); 21301592Srgrimes 21311592Srgrimes case TYPE_E: 21321592Srgrimes reply(553, "TYPE E not implemented."); 21331592Srgrimes transflag = 0; 21341592Srgrimes return (-1); 21351592Srgrimes 21361592Srgrimes case TYPE_A: 21371592Srgrimes while ((c = getc(instr)) != EOF) { 213889935Syar if (recvurg) 213989935Syar goto got_oob; 21401592Srgrimes byte_count++; 21411592Srgrimes if (c == '\n') 21421592Srgrimes bare_lfs++; 21431592Srgrimes while (c == '\r') { 21441592Srgrimes if (ferror(outstr)) 21451592Srgrimes goto data_err; 21461592Srgrimes if ((c = getc(instr)) != '\n') { 21471592Srgrimes (void) putc ('\r', outstr); 21481592Srgrimes if (c == '\0' || c == EOF) 21491592Srgrimes goto contin2; 21501592Srgrimes } 21511592Srgrimes } 21521592Srgrimes (void) putc(c, outstr); 21531592Srgrimes contin2: ; 21541592Srgrimes } 215589935Syar if (recvurg) 215689935Syar goto got_oob; 21571592Srgrimes fflush(outstr); 21581592Srgrimes if (ferror(instr)) 21591592Srgrimes goto data_err; 21601592Srgrimes if (ferror(outstr)) 21611592Srgrimes goto file_err; 21621592Srgrimes transflag = 0; 21631592Srgrimes if (bare_lfs) { 21641592Srgrimes lreply(226, 21651592Srgrimes "WARNING! %d bare linefeeds received in ASCII mode", 21661592Srgrimes bare_lfs); 21671592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 21681592Srgrimes } 21691592Srgrimes return (0); 21701592Srgrimes default: 21711592Srgrimes reply(550, "Unimplemented TYPE %d in receive_data", type); 21721592Srgrimes transflag = 0; 21731592Srgrimes return (-1); 21741592Srgrimes } 21751592Srgrimes 21761592Srgrimesdata_err: 21771592Srgrimes transflag = 0; 21781592Srgrimes perror_reply(426, "Data Connection"); 21791592Srgrimes return (-1); 21801592Srgrimes 21811592Srgrimesfile_err: 21821592Srgrimes transflag = 0; 21831592Srgrimes perror_reply(452, "Error writing file"); 21841592Srgrimes return (-1); 218589935Syar 218689935Syargot_oob: 218789935Syar myoob(); 218889935Syar recvurg = 0; 218989935Syar transflag = 0; 219089935Syar return (-1); 21911592Srgrimes} 21921592Srgrimes 21931592Srgrimesvoid 219490148Simpstatfilecmd(char *filename) 21951592Srgrimes{ 21961592Srgrimes FILE *fin; 2197109382Syar int atstart; 21981592Srgrimes int c; 21991592Srgrimes char line[LINE_MAX]; 22001592Srgrimes 220125165Sdavidn (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 22021592Srgrimes fin = ftpd_popen(line, "r"); 22031592Srgrimes lreply(211, "status of %s:", filename); 2204109382Syar atstart = 1; 22051592Srgrimes while ((c = getc(fin)) != EOF) { 22061592Srgrimes if (c == '\n') { 22071592Srgrimes if (ferror(stdout)){ 22081592Srgrimes perror_reply(421, "control connection"); 22091592Srgrimes (void) ftpd_pclose(fin); 22101592Srgrimes dologout(1); 22111592Srgrimes /* NOTREACHED */ 22121592Srgrimes } 22131592Srgrimes if (ferror(fin)) { 22141592Srgrimes perror_reply(551, filename); 22151592Srgrimes (void) ftpd_pclose(fin); 22161592Srgrimes return; 22171592Srgrimes } 22181592Srgrimes (void) putc('\r', stdout); 22191592Srgrimes } 2220109382Syar /* 2221109382Syar * RFC 959 says neutral text should be prepended before 2222109382Syar * a leading 3-digit number followed by whitespace, but 2223109382Syar * many ftp clients can be confused by any leading digits, 2224109382Syar * as a matter of fact. 2225109382Syar */ 2226109382Syar if (atstart && isdigit(c)) 2227109382Syar (void) putc(' ', stdout); 22281592Srgrimes (void) putc(c, stdout); 2229109382Syar atstart = (c == '\n'); 22301592Srgrimes } 22311592Srgrimes (void) ftpd_pclose(fin); 22321592Srgrimes reply(211, "End of Status"); 22331592Srgrimes} 22341592Srgrimes 22351592Srgrimesvoid 223690148Simpstatcmd(void) 22371592Srgrimes{ 223856668Sshin union sockunion *su; 22391592Srgrimes u_char *a, *p; 224099255Sume char hname[NI_MAXHOST]; 224156668Sshin int ispassive; 22421592Srgrimes 2243110037Syar if (hostinfo) { 2244110037Syar lreply(211, "%s FTP server status:", hostname); 2245110037Syar printf(" %s\r\n", version); 2246110037Syar } else 2247110037Syar lreply(211, "FTP server status:"); 22481592Srgrimes printf(" Connected to %s", remotehost); 224956668Sshin if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 225099255Sume hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 225156668Sshin if (strcmp(hname, remotehost) != 0) 225256668Sshin printf(" (%s)", hname); 225356668Sshin } 22541592Srgrimes printf("\r\n"); 22551592Srgrimes if (logged_in) { 22561592Srgrimes if (guest) 22571592Srgrimes printf(" Logged in anonymously\r\n"); 22581592Srgrimes else 22591592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 22601592Srgrimes } else if (askpasswd) 22611592Srgrimes printf(" Waiting for password\r\n"); 22621592Srgrimes else 22631592Srgrimes printf(" Waiting for user name\r\n"); 22641592Srgrimes printf(" TYPE: %s", typenames[type]); 22651592Srgrimes if (type == TYPE_A || type == TYPE_E) 22661592Srgrimes printf(", FORM: %s", formnames[form]); 22671592Srgrimes if (type == TYPE_L) 2268103949Smike#if CHAR_BIT == 8 2269103949Smike printf(" %d", CHAR_BIT); 22701592Srgrimes#else 22711592Srgrimes printf(" %d", bytesize); /* need definition! */ 22721592Srgrimes#endif 22731592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 22741592Srgrimes strunames[stru], modenames[mode]); 22751592Srgrimes if (data != -1) 22761592Srgrimes printf(" Data connection open\r\n"); 22771592Srgrimes else if (pdata != -1) { 227856668Sshin ispassive = 1; 227956668Sshin su = &pasv_addr; 22801592Srgrimes goto printaddr; 22811592Srgrimes } else if (usedefault == 0) { 228256668Sshin ispassive = 0; 228356668Sshin su = &data_dest; 22841592Srgrimesprintaddr: 22851592Srgrimes#define UC(b) (((int) b) & 0xff) 228656668Sshin if (epsvall) { 228756668Sshin printf(" EPSV only mode (EPSV ALL)\r\n"); 228856668Sshin goto epsvonly; 228956668Sshin } 229056668Sshin 229156668Sshin /* PORT/PASV */ 229256668Sshin if (su->su_family == AF_INET) { 229356668Sshin a = (u_char *) &su->su_sin.sin_addr; 229456668Sshin p = (u_char *) &su->su_sin.sin_port; 229556668Sshin printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 229656668Sshin ispassive ? "PASV" : "PORT", 229756668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 229856668Sshin UC(p[0]), UC(p[1])); 229956668Sshin } 230056668Sshin 230156668Sshin /* LPRT/LPSV */ 230256668Sshin { 230356668Sshin int alen, af, i; 230456668Sshin 230556668Sshin switch (su->su_family) { 230656668Sshin case AF_INET: 230756668Sshin a = (u_char *) &su->su_sin.sin_addr; 230856668Sshin p = (u_char *) &su->su_sin.sin_port; 230956668Sshin alen = sizeof(su->su_sin.sin_addr); 231056668Sshin af = 4; 231156668Sshin break; 231256668Sshin case AF_INET6: 231356668Sshin a = (u_char *) &su->su_sin6.sin6_addr; 231456668Sshin p = (u_char *) &su->su_sin6.sin6_port; 231556668Sshin alen = sizeof(su->su_sin6.sin6_addr); 231656668Sshin af = 6; 231756668Sshin break; 231856668Sshin default: 231956668Sshin af = 0; 232056668Sshin break; 232156668Sshin } 232256668Sshin if (af) { 232356668Sshin printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 232456668Sshin af, alen); 232556668Sshin for (i = 0; i < alen; i++) 232656668Sshin printf("%d,", UC(a[i])); 232756668Sshin printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 232856668Sshin } 232956668Sshin } 233056668Sshin 233156668Sshinepsvonly:; 233256668Sshin /* EPRT/EPSV */ 233356668Sshin { 233456668Sshin int af; 233556668Sshin 233656668Sshin switch (su->su_family) { 233756668Sshin case AF_INET: 233856668Sshin af = 1; 233956668Sshin break; 234056668Sshin case AF_INET6: 234156668Sshin af = 2; 234256668Sshin break; 234356668Sshin default: 234456668Sshin af = 0; 234556668Sshin break; 234656668Sshin } 234756668Sshin if (af) { 234899255Sume union sockunion tmp; 234999255Sume 235099255Sume tmp = *su; 235199255Sume if (tmp.su_family == AF_INET6) 235299255Sume tmp.su_sin6.sin6_scope_id = 0; 235399255Sume if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 235456668Sshin hname, sizeof(hname) - 1, NULL, 0, 235556668Sshin NI_NUMERICHOST)) { 235656668Sshin printf(" %s |%d|%s|%d|\r\n", 235756668Sshin ispassive ? "EPSV" : "EPRT", 235899255Sume af, hname, htons(tmp.su_port)); 235956668Sshin } 236056668Sshin } 236156668Sshin } 23621592Srgrimes#undef UC 23631592Srgrimes } else 23641592Srgrimes printf(" No data connection\r\n"); 23651592Srgrimes reply(211, "End of status"); 23661592Srgrimes} 23671592Srgrimes 23681592Srgrimesvoid 236990148Simpfatalerror(char *s) 23701592Srgrimes{ 23711592Srgrimes 23721592Srgrimes reply(451, "Error in server: %s\n", s); 23731592Srgrimes reply(221, "Closing connection due to server error."); 23741592Srgrimes dologout(0); 23751592Srgrimes /* NOTREACHED */ 23761592Srgrimes} 23771592Srgrimes 23781592Srgrimesvoid 23791592Srgrimesreply(int n, const char *fmt, ...) 23801592Srgrimes{ 23811592Srgrimes va_list ap; 238290148Simp 2383129170Stjr (void)printf("%d ", n); 23841592Srgrimes va_start(ap, fmt); 23851592Srgrimes (void)vprintf(fmt, ap); 2386129170Stjr va_end(ap); 23871592Srgrimes (void)printf("\r\n"); 23881592Srgrimes (void)fflush(stdout); 238976096Smarkm if (ftpdebug) { 23901592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 2391129170Stjr va_start(ap, fmt); 23921592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2393129170Stjr va_end(ap); 23941592Srgrimes } 23951592Srgrimes} 23961592Srgrimes 23971592Srgrimesvoid 23981592Srgrimeslreply(int n, const char *fmt, ...) 23991592Srgrimes{ 24001592Srgrimes va_list ap; 240190148Simp 2402129170Stjr (void)printf("%d- ", n); 24031592Srgrimes va_start(ap, fmt); 24041592Srgrimes (void)vprintf(fmt, ap); 2405129170Stjr va_end(ap); 24061592Srgrimes (void)printf("\r\n"); 24071592Srgrimes (void)fflush(stdout); 240876096Smarkm if (ftpdebug) { 24091592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 2410129170Stjr va_start(ap, fmt); 24111592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 2412129170Stjr va_end(ap); 24131592Srgrimes } 24141592Srgrimes} 24151592Srgrimes 24161592Srgrimesstatic void 241790148Simpack(char *s) 24181592Srgrimes{ 24191592Srgrimes 24201592Srgrimes reply(250, "%s command successful.", s); 24211592Srgrimes} 24221592Srgrimes 24231592Srgrimesvoid 242490148Simpnack(char *s) 24251592Srgrimes{ 24261592Srgrimes 24271592Srgrimes reply(502, "%s command not implemented.", s); 24281592Srgrimes} 24291592Srgrimes 24301592Srgrimes/* ARGSUSED */ 24311592Srgrimesvoid 243290148Simpyyerror(char *s) 24331592Srgrimes{ 24341592Srgrimes char *cp; 24351592Srgrimes 243617478Smarkm if ((cp = strchr(cbuf,'\n'))) 24371592Srgrimes *cp = '\0'; 24381592Srgrimes reply(500, "'%s': command not understood.", cbuf); 24391592Srgrimes} 24401592Srgrimes 24411592Srgrimesvoid 244290148Simpdelete(char *name) 24431592Srgrimes{ 24441592Srgrimes struct stat st; 24451592Srgrimes 24461592Srgrimes LOGCMD("delete", name); 2447100439Syar if (lstat(name, &st) < 0) { 24481592Srgrimes perror_reply(550, name); 24491592Srgrimes return; 24501592Srgrimes } 24511592Srgrimes if ((st.st_mode&S_IFMT) == S_IFDIR) { 24521592Srgrimes if (rmdir(name) < 0) { 24531592Srgrimes perror_reply(550, name); 24541592Srgrimes return; 24551592Srgrimes } 24561592Srgrimes goto done; 24571592Srgrimes } 2458125568Syar if (guest && noguestmod) { 2459125568Syar reply(550, "Operation not permitted"); 2460125568Syar return; 2461125568Syar } 2462125568Syar if (unlink(name) < 0) { 24631592Srgrimes perror_reply(550, name); 24641592Srgrimes return; 24651592Srgrimes } 24661592Srgrimesdone: 24671592Srgrimes ack("DELE"); 24681592Srgrimes} 24691592Srgrimes 24701592Srgrimesvoid 247190148Simpcwd(char *path) 24721592Srgrimes{ 24731592Srgrimes 24741592Srgrimes if (chdir(path) < 0) 24751592Srgrimes perror_reply(550, path); 24761592Srgrimes else 24771592Srgrimes ack("CWD"); 24781592Srgrimes} 24791592Srgrimes 24801592Srgrimesvoid 248190148Simpmakedir(char *name) 24821592Srgrimes{ 2483100878Syar char *s; 24841592Srgrimes 24851592Srgrimes LOGCMD("mkdir", name); 248699195Smdodd if (guest && noguestmkd) 248799195Smdodd reply(550, "%s: permission denied", name); 248899195Smdodd else if (mkdir(name, 0777) < 0) 24891592Srgrimes perror_reply(550, name); 2490100878Syar else { 2491100878Syar if ((s = doublequote(name)) == NULL) 2492100878Syar fatalerror("Ran out of memory."); 2493100878Syar reply(257, "\"%s\" directory created.", s); 2494100878Syar free(s); 2495100878Syar } 24961592Srgrimes} 24971592Srgrimes 24981592Srgrimesvoid 249990148Simpremovedir(char *name) 25001592Srgrimes{ 25011592Srgrimes 25021592Srgrimes LOGCMD("rmdir", name); 25031592Srgrimes if (rmdir(name) < 0) 25041592Srgrimes perror_reply(550, name); 25051592Srgrimes else 25061592Srgrimes ack("RMD"); 25071592Srgrimes} 25081592Srgrimes 25091592Srgrimesvoid 251090148Simppwd(void) 25111592Srgrimes{ 2512100486Syar char *s, path[MAXPATHLEN + 1]; 25131592Srgrimes 25141592Srgrimes if (getwd(path) == (char *)NULL) 25151592Srgrimes reply(550, "%s.", path); 2516100486Syar else { 2517100486Syar if ((s = doublequote(path)) == NULL) 2518100486Syar fatalerror("Ran out of memory."); 2519100486Syar reply(257, "\"%s\" is current directory.", s); 2520100486Syar free(s); 2521100486Syar } 25221592Srgrimes} 25231592Srgrimes 25241592Srgrimeschar * 252590148Simprenamefrom(char *name) 25261592Srgrimes{ 25271592Srgrimes struct stat st; 25281592Srgrimes 2529125569Syar if (guest && noguestmod) { 2530125569Syar reply(550, "Operation not permitted"); 2531125569Syar return (NULL); 2532125569Syar } 2533100439Syar if (lstat(name, &st) < 0) { 25341592Srgrimes perror_reply(550, name); 2535125570Syar return (NULL); 25361592Srgrimes } 25371592Srgrimes reply(350, "File exists, ready for destination name"); 25381592Srgrimes return (name); 25391592Srgrimes} 25401592Srgrimes 25411592Srgrimesvoid 254290148Simprenamecmd(char *from, char *to) 25431592Srgrimes{ 254417433Spst struct stat st; 25451592Srgrimes 25461592Srgrimes LOGCMD2("rename", from, to); 254717433Spst 254817433Spst if (guest && (stat(to, &st) == 0)) { 254917433Spst reply(550, "%s: permission denied", to); 255017433Spst return; 255117433Spst } 255217433Spst 25531592Srgrimes if (rename(from, to) < 0) 25541592Srgrimes perror_reply(550, "rename"); 25551592Srgrimes else 25561592Srgrimes ack("RNTO"); 25571592Srgrimes} 25581592Srgrimes 25591592Srgrimesstatic void 256090148Simpdolog(struct sockaddr *who) 25611592Srgrimes{ 256256668Sshin int error; 25631592Srgrimes 256456668Sshin realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 256556668Sshin 25661592Srgrimes#ifdef SETPROCTITLE 256725283Sdavidn#ifdef VIRTUAL_HOSTING 256825283Sdavidn if (thishost != firsthost) 256925283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 257025283Sdavidn remotehost, hostname); 257125283Sdavidn else 257225283Sdavidn#endif 257325283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected", 257425283Sdavidn remotehost); 257513139Speter setproctitle("%s", proctitle); 25761592Srgrimes#endif /* SETPROCTITLE */ 25771592Srgrimes 257825283Sdavidn if (logging) { 257925283Sdavidn#ifdef VIRTUAL_HOSTING 258025283Sdavidn if (thishost != firsthost) 258125283Sdavidn syslog(LOG_INFO, "connection from %s (to %s)", 258225283Sdavidn remotehost, hostname); 258325283Sdavidn else 258425283Sdavidn#endif 258556668Sshin { 258656668Sshin char who_name[MAXHOSTNAMELEN]; 258756668Sshin 258856668Sshin error = getnameinfo(who, who->sa_len, 258956668Sshin who_name, sizeof(who_name) - 1, 259099255Sume NULL, 0, NI_NUMERICHOST); 259133782Seivind syslog(LOG_INFO, "connection from %s (%s)", remotehost, 259256668Sshin error == 0 ? who_name : ""); 259356668Sshin } 259425283Sdavidn } 25951592Srgrimes} 25961592Srgrimes 25971592Srgrimes/* 25981592Srgrimes * Record logout in wtmp file 25991592Srgrimes * and exit with supplied status. 26001592Srgrimes */ 26011592Srgrimesvoid 260290148Simpdologout(int status) 26031592Srgrimes{ 260422057Sdg /* 260522057Sdg * Prevent reception of SIGURG from resulting in a resumption 260622057Sdg * back to the main program loop. 260722058Sdg */ 260822057Sdg transflag = 0; 26091592Srgrimes 2610102311Syar if (logged_in && dowtmp) { 26111592Srgrimes (void) seteuid((uid_t)0); 261289920Sume ftpd_logwtmp(ttyline, "", NULL); 26131592Srgrimes } 26141592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 26151592Srgrimes _exit(status); 26161592Srgrimes} 26171592Srgrimes 26181592Srgrimesstatic void 261990148Simpsigurg(int signo) 26201592Srgrimes{ 262189935Syar 262289935Syar recvurg = 1; 262389935Syar} 262489935Syar 262589935Syarstatic void 262690148Simpmyoob(void) 262789935Syar{ 26281592Srgrimes char *cp; 26291592Srgrimes 26301592Srgrimes /* only process if transfer occurring */ 26311592Srgrimes if (!transflag) 26321592Srgrimes return; 26331592Srgrimes cp = tmpline; 26341592Srgrimes if (getline(cp, 7, stdin) == NULL) { 26351592Srgrimes reply(221, "You could at least say goodbye."); 26361592Srgrimes dologout(0); 26371592Srgrimes } 26381592Srgrimes upper(cp); 26391592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 26401592Srgrimes tmpline[0] = '\0'; 26411592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 26421592Srgrimes reply(226, "Abort successful"); 26431592Srgrimes } 26441592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 264551192Smharo tmpline[0] = '\0'; 26461592Srgrimes if (file_size != (off_t) -1) 26471592Srgrimes reply(213, "Status: %qd of %qd bytes transferred", 26481592Srgrimes byte_count, file_size); 26491592Srgrimes else 26501592Srgrimes reply(213, "Status: %qd bytes transferred", byte_count); 26511592Srgrimes } 26521592Srgrimes} 26531592Srgrimes 26541592Srgrimes/* 26551592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 26561592Srgrimes * the PASV command in RFC959. However, it has been blessed as 26571592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 26581592Srgrimes * with Rick Adams on 25 Jan 89. 26591592Srgrimes */ 26601592Srgrimesvoid 266190148Simppassive(void) 26621592Srgrimes{ 2663100615Syar int len, on; 26641592Srgrimes char *p, *a; 26651592Srgrimes 266617433Spst if (pdata >= 0) /* close old port if one set */ 266717433Spst close(pdata); 266817433Spst 266956668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 26701592Srgrimes if (pdata < 0) { 26711592Srgrimes perror_reply(425, "Can't open passive connection"); 26721592Srgrimes return; 26731592Srgrimes } 2674100615Syar on = 1; 2675100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2676100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 26779933Spst 267817433Spst (void) seteuid((uid_t)0); 267917433Spst 268019903Spst#ifdef IP_PORTRANGE 268156668Sshin if (ctrl_addr.su_family == AF_INET) { 2682100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2683100615Syar : IP_PORTRANGE_DEFAULT; 268419903Spst 268519903Spst if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2686100612Syar &on, sizeof(on)) < 0) 268719903Spst goto pasv_error; 26881592Srgrimes } 268919903Spst#endif 269060929Snsayer#ifdef IPV6_PORTRANGE 269160929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2692100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2693100615Syar : IPV6_PORTRANGE_DEFAULT; 26949933Spst 269560929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2696100612Syar &on, sizeof(on)) < 0) 269760929Snsayer goto pasv_error; 269860929Snsayer } 269960929Snsayer#endif 270060929Snsayer 270116033Speter pasv_addr = ctrl_addr; 270256668Sshin pasv_addr.su_port = 0; 270356668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 270416033Speter goto pasv_error; 270517433Spst 270616033Speter (void) seteuid((uid_t)pw->pw_uid); 270716033Speter 27081592Srgrimes len = sizeof(pasv_addr); 27091592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 27101592Srgrimes goto pasv_error; 27111592Srgrimes if (listen(pdata, 1) < 0) 27121592Srgrimes goto pasv_error; 271356668Sshin if (pasv_addr.su_family == AF_INET) 271456668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 271556668Sshin else if (pasv_addr.su_family == AF_INET6 && 271656668Sshin IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 271756668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 271856668Sshin else 271956668Sshin goto pasv_error; 272056668Sshin 272156668Sshin p = (char *) &pasv_addr.su_port; 27221592Srgrimes 27231592Srgrimes#define UC(b) (((int) b) & 0xff) 27241592Srgrimes 27251592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 27261592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 27271592Srgrimes return; 27281592Srgrimes 27291592Srgrimespasv_error: 273017433Spst (void) seteuid((uid_t)pw->pw_uid); 27311592Srgrimes (void) close(pdata); 27321592Srgrimes pdata = -1; 27331592Srgrimes perror_reply(425, "Can't open passive connection"); 27341592Srgrimes return; 27351592Srgrimes} 27361592Srgrimes 27371592Srgrimes/* 273856668Sshin * Long Passive defined in RFC 1639. 273956668Sshin * 228 Entering Long Passive Mode 274056668Sshin * (af, hal, h1, h2, h3,..., pal, p1, p2...) 274156668Sshin */ 274256668Sshin 274356668Sshinvoid 274490148Simplong_passive(char *cmd, int pf) 274556668Sshin{ 2746100615Syar int len, on; 274756668Sshin char *p, *a; 274856668Sshin 274956668Sshin if (pdata >= 0) /* close old port if one set */ 275056668Sshin close(pdata); 275156668Sshin 275256668Sshin if (pf != PF_UNSPEC) { 275356668Sshin if (ctrl_addr.su_family != pf) { 275456668Sshin switch (ctrl_addr.su_family) { 275556668Sshin case AF_INET: 275656668Sshin pf = 1; 275756668Sshin break; 275856668Sshin case AF_INET6: 275956668Sshin pf = 2; 276056668Sshin break; 276156668Sshin default: 276256668Sshin pf = 0; 276356668Sshin break; 276456668Sshin } 276556668Sshin /* 276656668Sshin * XXX 276756668Sshin * only EPRT/EPSV ready clients will understand this 276856668Sshin */ 276956668Sshin if (strcmp(cmd, "EPSV") == 0 && pf) { 277056668Sshin reply(522, "Network protocol mismatch, " 277156668Sshin "use (%d)", pf); 277256668Sshin } else 277356668Sshin reply(501, "Network protocol mismatch"); /*XXX*/ 277456668Sshin 277556668Sshin return; 277656668Sshin } 277756668Sshin } 277856668Sshin 277956668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 278056668Sshin if (pdata < 0) { 278156668Sshin perror_reply(425, "Can't open passive connection"); 278256668Sshin return; 278356668Sshin } 2784100615Syar on = 1; 2785100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2786100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 278756668Sshin 278856668Sshin (void) seteuid((uid_t)0); 278956668Sshin 279056668Sshin pasv_addr = ctrl_addr; 279156668Sshin pasv_addr.su_port = 0; 279256668Sshin len = pasv_addr.su_len; 279356668Sshin 279460929Snsayer#ifdef IP_PORTRANGE 279560929Snsayer if (ctrl_addr.su_family == AF_INET) { 2796100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2797100615Syar : IP_PORTRANGE_DEFAULT; 279860929Snsayer 279960929Snsayer if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2800100612Syar &on, sizeof(on)) < 0) 280160929Snsayer goto pasv_error; 280260929Snsayer } 280360929Snsayer#endif 280460929Snsayer#ifdef IPV6_PORTRANGE 280560929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2806100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2807100615Syar : IPV6_PORTRANGE_DEFAULT; 280860929Snsayer 280960929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2810100612Syar &on, sizeof(on)) < 0) 281160929Snsayer goto pasv_error; 281260929Snsayer } 281360929Snsayer#endif 281460929Snsayer 281556668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 281656668Sshin goto pasv_error; 281756668Sshin 281856668Sshin (void) seteuid((uid_t)pw->pw_uid); 281956668Sshin 282056668Sshin if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 282156668Sshin goto pasv_error; 282256668Sshin if (listen(pdata, 1) < 0) 282356668Sshin goto pasv_error; 282456668Sshin 282556668Sshin#define UC(b) (((int) b) & 0xff) 282656668Sshin 282756668Sshin if (strcmp(cmd, "LPSV") == 0) { 282856668Sshin p = (char *)&pasv_addr.su_port; 282956668Sshin switch (pasv_addr.su_family) { 283056668Sshin case AF_INET: 283156668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 283256668Sshin v4_reply: 283356668Sshin reply(228, 283456668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 283556668Sshin 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 283656668Sshin 2, UC(p[0]), UC(p[1])); 283756668Sshin return; 283856668Sshin case AF_INET6: 283956668Sshin if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 284056668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 284156668Sshin goto v4_reply; 284256668Sshin } 284356668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr; 284456668Sshin reply(228, 284556668Sshin"Entering Long Passive Mode " 284656668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 284756668Sshin 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 284856668Sshin UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 284956668Sshin UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 285056668Sshin UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 285156668Sshin 2, UC(p[0]), UC(p[1])); 285256668Sshin return; 285356668Sshin } 285456668Sshin } else if (strcmp(cmd, "EPSV") == 0) { 285556668Sshin switch (pasv_addr.su_family) { 285656668Sshin case AF_INET: 285756668Sshin case AF_INET6: 285856668Sshin reply(229, "Entering Extended Passive Mode (|||%d|)", 285956668Sshin ntohs(pasv_addr.su_port)); 286056668Sshin return; 286156668Sshin } 286256668Sshin } else { 286356668Sshin /* more proper error code? */ 286456668Sshin } 286556668Sshin 286656668Sshinpasv_error: 286756668Sshin (void) seteuid((uid_t)pw->pw_uid); 286856668Sshin (void) close(pdata); 286956668Sshin pdata = -1; 287056668Sshin perror_reply(425, "Can't open passive connection"); 287156668Sshin return; 287256668Sshin} 287356668Sshin 287456668Sshin/* 2875101537Syar * Generate unique name for file with basename "local" 2876101537Syar * and open the file in order to avoid possible races. 2877101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 2878101537Syar * Return descriptor to the file, set "name" to its name. 2879101537Syar * 28801592Srgrimes * Generates failure reply on error. 28811592Srgrimes */ 2882101537Syarstatic int 2883101537Syarguniquefd(char *local, char **name) 28841592Srgrimes{ 28851592Srgrimes static char new[MAXPATHLEN]; 28861592Srgrimes struct stat st; 2887101537Syar char *cp; 28881592Srgrimes int count; 2889101537Syar int fd; 28901592Srgrimes 28911592Srgrimes cp = strrchr(local, '/'); 28921592Srgrimes if (cp) 28931592Srgrimes *cp = '\0'; 28941592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 28951592Srgrimes perror_reply(553, cp ? local : "."); 2896101537Syar return (-1); 28971592Srgrimes } 2898101537Syar if (cp) { 2899101537Syar /* 2900101537Syar * Let not overwrite dirname with counter suffix. 2901101537Syar * -4 is for /nn\0 2902101537Syar * In this extreme case dot won't be put in front of suffix. 2903101537Syar */ 2904101537Syar if (strlen(local) > sizeof(new) - 4) { 2905101537Syar reply(553, "Pathname too long"); 2906101537Syar return (-1); 2907101537Syar } 29081592Srgrimes *cp = '/'; 2909101537Syar } 291031973Simp /* -4 is for the .nn<null> we put on the end below */ 291131973Simp (void) snprintf(new, sizeof(new) - 4, "%s", local); 29121592Srgrimes cp = new + strlen(new); 2913101537Syar /* 2914101537Syar * Don't generate dotfile unless requested explicitly. 2915101537Syar * This covers the case when basename gets truncated off 2916101537Syar * by buffer size. 2917101537Syar */ 2918101537Syar if (cp > new && cp[-1] != '/') 2919101537Syar *cp++ = '.'; 2920101537Syar for (count = 0; count < 100; count++) { 2921101537Syar /* At count 0 try unmodified name */ 2922101537Syar if (count) 2923101537Syar (void)sprintf(cp, "%d", count); 2924101537Syar if ((fd = open(count ? new : local, 2925101537Syar O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 2926101537Syar *name = count ? new : local; 2927101537Syar return (fd); 2928101537Syar } 2929110046Syar if (errno != EEXIST) { 2930110307Syar perror_reply(553, count ? new : local); 2931110046Syar return (-1); 2932110046Syar } 29331592Srgrimes } 29341592Srgrimes reply(452, "Unique file name cannot be created."); 2935101537Syar return (-1); 29361592Srgrimes} 29371592Srgrimes 29381592Srgrimes/* 29391592Srgrimes * Format and send reply containing system error number. 29401592Srgrimes */ 29411592Srgrimesvoid 294290148Simpperror_reply(int code, char *string) 29431592Srgrimes{ 29441592Srgrimes 29451592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 29461592Srgrimes} 29471592Srgrimes 29481592Srgrimesstatic char *onefile[] = { 29491592Srgrimes "", 29501592Srgrimes 0 29511592Srgrimes}; 29521592Srgrimes 29531592Srgrimesvoid 295490148Simpsend_file_list(char *whichf) 29551592Srgrimes{ 29561592Srgrimes struct stat st; 29571592Srgrimes DIR *dirp = NULL; 29581592Srgrimes struct dirent *dir; 29591592Srgrimes FILE *dout = NULL; 29601592Srgrimes char **dirlist, *dirname; 29611592Srgrimes int simple = 0; 29621592Srgrimes int freeglob = 0; 29631592Srgrimes glob_t gl; 29641592Srgrimes 29651592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 2966100222Smikeh int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 29671592Srgrimes 29681592Srgrimes memset(&gl, 0, sizeof(gl)); 296974470Sjlemon gl.gl_matchc = MAXGLOBARGS; 297080525Smikeh flags |= GLOB_LIMIT; 29711592Srgrimes freeglob = 1; 29721592Srgrimes if (glob(whichf, flags, 0, &gl)) { 29731592Srgrimes reply(550, "not found"); 29741592Srgrimes goto out; 29751592Srgrimes } else if (gl.gl_pathc == 0) { 29761592Srgrimes errno = ENOENT; 29771592Srgrimes perror_reply(550, whichf); 29781592Srgrimes goto out; 29791592Srgrimes } 29801592Srgrimes dirlist = gl.gl_pathv; 29811592Srgrimes } else { 29821592Srgrimes onefile[0] = whichf; 29831592Srgrimes dirlist = onefile; 29841592Srgrimes simple = 1; 29851592Srgrimes } 29861592Srgrimes 298717478Smarkm while ((dirname = *dirlist++)) { 29881592Srgrimes if (stat(dirname, &st) < 0) { 29891592Srgrimes /* 29901592Srgrimes * If user typed "ls -l", etc, and the client 29911592Srgrimes * used NLST, do what the user meant. 29921592Srgrimes */ 29931592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 29941592Srgrimes transflag == 0) { 299525165Sdavidn retrieve(_PATH_LS " %s", dirname); 29961592Srgrimes goto out; 29971592Srgrimes } 29981592Srgrimes perror_reply(550, whichf); 29991592Srgrimes if (dout != NULL) { 30001592Srgrimes (void) fclose(dout); 30011592Srgrimes transflag = 0; 30021592Srgrimes data = -1; 30031592Srgrimes pdata = -1; 30041592Srgrimes } 30051592Srgrimes goto out; 30061592Srgrimes } 30071592Srgrimes 30081592Srgrimes if (S_ISREG(st.st_mode)) { 30091592Srgrimes if (dout == NULL) { 30101592Srgrimes dout = dataconn("file list", (off_t)-1, "w"); 30111592Srgrimes if (dout == NULL) 30121592Srgrimes goto out; 30131592Srgrimes transflag++; 30141592Srgrimes } 30151592Srgrimes fprintf(dout, "%s%s\n", dirname, 30161592Srgrimes type == TYPE_A ? "\r" : ""); 30171592Srgrimes byte_count += strlen(dirname) + 1; 30181592Srgrimes continue; 30191592Srgrimes } else if (!S_ISDIR(st.st_mode)) 30201592Srgrimes continue; 30211592Srgrimes 30221592Srgrimes if ((dirp = opendir(dirname)) == NULL) 30231592Srgrimes continue; 30241592Srgrimes 30251592Srgrimes while ((dir = readdir(dirp)) != NULL) { 30261592Srgrimes char nbuf[MAXPATHLEN]; 30271592Srgrimes 302889935Syar if (recvurg) { 302989935Syar myoob(); 303089935Syar recvurg = 0; 303189935Syar transflag = 0; 303289935Syar goto out; 303389935Syar } 303489935Syar 30351592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 30361592Srgrimes continue; 30371592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 30381592Srgrimes dir->d_namlen == 2) 30391592Srgrimes continue; 30401592Srgrimes 304199213Smaxim snprintf(nbuf, sizeof(nbuf), 304231973Simp "%s/%s", dirname, dir->d_name); 30431592Srgrimes 30441592Srgrimes /* 30451592Srgrimes * We have to do a stat to insure it's 30461592Srgrimes * not a directory or special file. 30471592Srgrimes */ 30481592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 30491592Srgrimes S_ISREG(st.st_mode))) { 30501592Srgrimes if (dout == NULL) { 30511592Srgrimes dout = dataconn("file list", (off_t)-1, 30521592Srgrimes "w"); 30531592Srgrimes if (dout == NULL) 30541592Srgrimes goto out; 30551592Srgrimes transflag++; 30561592Srgrimes } 30571592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 30581592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 30591592Srgrimes type == TYPE_A ? "\r" : ""); 30601592Srgrimes else 30611592Srgrimes fprintf(dout, "%s%s\n", nbuf, 30621592Srgrimes type == TYPE_A ? "\r" : ""); 30631592Srgrimes byte_count += strlen(nbuf) + 1; 30641592Srgrimes } 30651592Srgrimes } 30661592Srgrimes (void) closedir(dirp); 30671592Srgrimes } 30681592Srgrimes 30691592Srgrimes if (dout == NULL) 30701592Srgrimes reply(550, "No files found."); 30711592Srgrimes else if (ferror(dout) != 0) 30721592Srgrimes perror_reply(550, "Data connection"); 30731592Srgrimes else 30741592Srgrimes reply(226, "Transfer complete."); 30751592Srgrimes 30761592Srgrimes transflag = 0; 30771592Srgrimes if (dout != NULL) 30781592Srgrimes (void) fclose(dout); 30791592Srgrimes data = -1; 30801592Srgrimes pdata = -1; 30811592Srgrimesout: 30821592Srgrimes if (freeglob) { 30831592Srgrimes freeglob = 0; 30841592Srgrimes globfree(&gl); 30851592Srgrimes } 30861592Srgrimes} 30871592Srgrimes 308815196Sdgvoid 308990148Simpreapchild(int signo) 309015196Sdg{ 309115196Sdg while (wait3(NULL, WNOHANG, NULL) > 0); 309215196Sdg} 309315196Sdg 309413139Speter#ifdef OLD_SETPROCTITLE 30951592Srgrimes/* 30961592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 30971592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 30981592Srgrimes * have much of an environment or arglist to overwrite. 30991592Srgrimes */ 31001592Srgrimesvoid 31011592Srgrimessetproctitle(const char *fmt, ...) 31021592Srgrimes{ 31031592Srgrimes int i; 31041592Srgrimes va_list ap; 31051592Srgrimes char *p, *bp, ch; 31061592Srgrimes char buf[LINE_MAX]; 31071592Srgrimes 31081592Srgrimes va_start(ap, fmt); 31091592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 31101592Srgrimes 31111592Srgrimes /* make ps print our process name */ 31121592Srgrimes p = Argv[0]; 31131592Srgrimes *p++ = '-'; 31141592Srgrimes 31151592Srgrimes i = strlen(buf); 31161592Srgrimes if (i > LastArgv - p - 2) { 31171592Srgrimes i = LastArgv - p - 2; 31181592Srgrimes buf[i] = '\0'; 31191592Srgrimes } 31201592Srgrimes bp = buf; 31211592Srgrimes while (ch = *bp++) 31221592Srgrimes if (ch != '\n' && ch != '\r') 31231592Srgrimes *p++ = ch; 31241592Srgrimes while (p < LastArgv) 31251592Srgrimes *p++ = ' '; 31261592Srgrimes} 312713139Speter#endif /* OLD_SETPROCTITLE */ 31286740Sguido 312917433Spststatic void 313090148Simplogxfer(char *name, off_t size, time_t start) 31316740Sguido{ 31326740Sguido char buf[1024]; 31336740Sguido char path[MAXPATHLEN + 1]; 313436612Sjb time_t now; 31356740Sguido 31366740Sguido if (statfd >= 0 && getwd(path) != NULL) { 31376740Sguido time(&now); 313882792Sache snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%qd!%ld\n", 31396740Sguido ctime(&now)+4, ident, remotehost, 314082792Sache path, name, (long long)size, 314182792Sache (long)(now - start + (now == start))); 31426740Sguido write(statfd, buf, strlen(buf)); 31436740Sguido } 31446740Sguido} 3145100486Syar 3146100486Syarstatic char * 3147100486Syardoublequote(char *s) 3148100486Syar{ 3149100486Syar int n; 3150100486Syar char *p, *s2; 3151100486Syar 3152100486Syar for (p = s, n = 0; *p; p++) 3153100486Syar if (*p == '"') 3154100486Syar n++; 3155100486Syar 3156100486Syar if ((s2 = malloc(p - s + n + 1)) == NULL) 3157100486Syar return (NULL); 3158100486Syar 3159100486Syar for (p = s2; *s; s++, p++) { 3160100486Syar if ((*p = *s) == '"') 3161100486Syar *(++p) = '"'; 3162100486Syar } 3163100486Syar *p = '\0'; 3164100486Syar 3165100486Syar return (s2); 3166100486Syar} 3167120059Sume 3168120059Sume/* setup server socket for specified address family */ 3169120059Sume/* if af is PF_UNSPEC more than one socket may be returned */ 3170120059Sume/* the returned list is dynamically allocated, so caller needs to free it */ 3171120059Sumestatic int * 3172120059Sumesocksetup(int af, char *bindname, const char *bindport) 3173120059Sume{ 3174120059Sume struct addrinfo hints, *res, *r; 3175120059Sume int error, maxs, *s, *socks; 3176120059Sume const int on = 1; 3177120059Sume 3178120059Sume memset(&hints, 0, sizeof(hints)); 3179120059Sume hints.ai_flags = AI_PASSIVE; 3180120059Sume hints.ai_family = af; 3181120059Sume hints.ai_socktype = SOCK_STREAM; 3182120059Sume error = getaddrinfo(bindname, bindport, &hints, &res); 3183120059Sume if (error) { 3184120059Sume syslog(LOG_ERR, "%s", gai_strerror(error)); 3185120059Sume if (error == EAI_SYSTEM) 3186120059Sume syslog(LOG_ERR, "%s", strerror(errno)); 3187120059Sume return NULL; 3188120059Sume } 3189120059Sume 3190120059Sume /* Count max number of sockets we may open */ 3191120059Sume for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 3192120059Sume ; 3193120059Sume socks = malloc((maxs + 1) * sizeof(int)); 3194120059Sume if (!socks) { 3195120059Sume freeaddrinfo(res); 3196120059Sume syslog(LOG_ERR, "couldn't allocate memory for sockets"); 3197120059Sume return NULL; 3198120059Sume } 3199120059Sume 3200120059Sume *socks = 0; /* num of sockets counter at start of array */ 3201120059Sume s = socks + 1; 3202120059Sume for (r = res; r; r = r->ai_next) { 3203120059Sume *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 3204120059Sume if (*s < 0) { 3205120059Sume syslog(LOG_DEBUG, "control socket: %m"); 3206120059Sume continue; 3207120059Sume } 3208120059Sume if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 3209120059Sume &on, sizeof(on)) < 0) 3210120059Sume syslog(LOG_WARNING, 3211120059Sume "control setsockopt (SO_REUSEADDR): %m"); 3212120059Sume if (r->ai_family == AF_INET6) { 3213120059Sume if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 3214120059Sume &on, sizeof(on)) < 0) 3215120059Sume syslog(LOG_WARNING, 3216120059Sume "control setsockopt (IPV6_V6ONLY): %m"); 3217120059Sume } 3218120059Sume if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 3219120059Sume syslog(LOG_DEBUG, "control bind: %m"); 3220120059Sume close(*s); 3221120059Sume continue; 3222120059Sume } 3223120059Sume (*socks)++; 3224120059Sume s++; 3225120059Sume } 3226120059Sume 3227120059Sume if (res) 3228120059Sume freeaddrinfo(res); 3229120059Sume 3230120059Sume if (*socks == 0) { 3231120059Sume syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); 3232120059Sume free(socks); 3233120059Sume return NULL; 3234120059Sume } 3235120059Sume return(socks); 3236120059Sume} 3237