ftpd.c revision 122751
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 122751 2003-11-15 11:08:26Z yar $"; 481592Srgrimes#endif /* not lint */ 491592Srgrimes 501592Srgrimes/* 511592Srgrimes * FTP server. 521592Srgrimes */ 531592Srgrimes#include <sys/param.h> 541592Srgrimes#include <sys/ioctl.h> 5566907Swollman#include <sys/mman.h> 561592Srgrimes#include <sys/socket.h> 5766907Swollman#include <sys/stat.h> 5866907Swollman#include <sys/time.h> 591592Srgrimes#include <sys/wait.h> 601592Srgrimes 611592Srgrimes#include <netinet/in.h> 621592Srgrimes#include <netinet/in_systm.h> 631592Srgrimes#include <netinet/ip.h> 648240Swollman#include <netinet/tcp.h> 651592Srgrimes 661592Srgrimes#define FTP_NAMES 671592Srgrimes#include <arpa/ftp.h> 681592Srgrimes#include <arpa/inet.h> 691592Srgrimes#include <arpa/telnet.h> 701592Srgrimes 711592Srgrimes#include <ctype.h> 721592Srgrimes#include <dirent.h> 731592Srgrimes#include <err.h> 741592Srgrimes#include <errno.h> 751592Srgrimes#include <fcntl.h> 761592Srgrimes#include <glob.h> 771592Srgrimes#include <limits.h> 781592Srgrimes#include <netdb.h> 791592Srgrimes#include <pwd.h> 8025187Sdavidn#include <grp.h> 8188763Sache#include <opie.h> 821592Srgrimes#include <signal.h> 831592Srgrimes#include <stdio.h> 841592Srgrimes#include <stdlib.h> 851592Srgrimes#include <string.h> 861592Srgrimes#include <syslog.h> 871592Srgrimes#include <time.h> 881592Srgrimes#include <unistd.h> 8913139Speter#include <libutil.h> 9025101Sdavidn#ifdef LOGIN_CAP 9125101Sdavidn#include <login_cap.h> 9225101Sdavidn#endif 931592Srgrimes 9474874Smarkm#ifdef USE_PAM 9551433Smarkm#include <security/pam_appl.h> 9651433Smarkm#endif 9751433Smarkm 981592Srgrimes#include "pathnames.h" 991592Srgrimes#include "extern.h" 1001592Srgrimes 1011592Srgrimes#include <stdarg.h> 1021592Srgrimes 10325165Sdavidnstatic char version[] = "Version 6.00LS"; 10425165Sdavidn#undef main 1051592Srgrimes 1061592Srgrimesextern off_t restart_point; 1071592Srgrimesextern char cbuf[]; 1081592Srgrimes 10956668Sshinunion sockunion 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; 10181592Srgrimes while ((cp = getusershell()) != NULL) 10191592Srgrimes if (strcmp(cp, shell) == 0) 10201592Srgrimes break; 10211592Srgrimes endusershell(); 10221592Srgrimes 1023109893Syar if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1, NULL)) { 10241592Srgrimes reply(530, "User %s access denied.", name); 10251592Srgrimes if (logging) 10261592Srgrimes syslog(LOG_NOTICE, 10271592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 10281592Srgrimes remotehost, name); 10291592Srgrimes pw = (struct passwd *) NULL; 10301592Srgrimes return; 10311592Srgrimes } 10321592Srgrimes } 10331592Srgrimes if (logging) 10341592Srgrimes strncpy(curname, name, sizeof(curname)-1); 103588763Sache 103688763Sache pwok = 0; 103779469Smarkm#ifdef USE_PAM 103879469Smarkm /* XXX Kluge! The conversation mechanism needs to be fixed. */ 103988763Sache#endif 104088763Sache if (opiechallenge(&opiedata, name, opieprompt) == 0) { 104188763Sache pwok = (pw != NULL) && 104288763Sache opieaccessfile(remotehost) && 104388763Sache opiealways(pw->pw_dir); 104488763Sache reply(331, "Response to %s %s for %s.", 104588763Sache opieprompt, pwok ? "requested" : "required", name); 104688763Sache } else { 104788763Sache pwok = 1; 104884146Sache reply(331, "Password required for %s.", name); 104988763Sache } 10501592Srgrimes askpasswd = 1; 10511592Srgrimes /* 10521592Srgrimes * Delay before reading passwd after first failed 10531592Srgrimes * attempt to slow down passwd-guessing programs. 10541592Srgrimes */ 10551592Srgrimes if (login_attempts) 10561592Srgrimes sleep((unsigned) login_attempts); 10571592Srgrimes} 10581592Srgrimes 10591592Srgrimes/* 1060109893Syar * Check if a user is in the file "fname", 1061109893Syar * return a pointer to a malloc'd string with the rest 1062109893Syar * of the matching line in "residue" if not NULL. 10631592Srgrimes */ 10641592Srgrimesstatic int 1065109893Syarcheckuser(char *fname, char *name, int pwset, char **residue) 10661592Srgrimes{ 10671592Srgrimes FILE *fd; 10681592Srgrimes int found = 0; 106999877Syar size_t len; 107099877Syar char *line, *mp, *p; 10711592Srgrimes 107217435Spst if ((fd = fopen(fname, "r")) != NULL) { 107399877Syar while (!found && (line = fgetln(fd, &len)) != NULL) { 107499877Syar /* skip comments */ 107599877Syar if (line[0] == '#') 107699877Syar continue; 107799877Syar if (line[len - 1] == '\n') { 107899877Syar line[len - 1] = '\0'; 107999877Syar mp = NULL; 108099877Syar } else { 108199877Syar if ((mp = malloc(len + 1)) == NULL) 108299877Syar fatalerror("Ran out of memory."); 108399877Syar memcpy(mp, line, len); 108499877Syar mp[len] = '\0'; 108599877Syar line = mp; 108699877Syar } 108799877Syar /* avoid possible leading and trailing whitespace */ 108899877Syar p = strtok(line, " \t"); 108999877Syar /* skip empty lines */ 109099877Syar if (p == NULL) 109199877Syar goto nextline; 109299877Syar /* 109399877Syar * if first chr is '@', check group membership 109499877Syar */ 109599877Syar if (p[0] == '@') { 109699877Syar int i = 0; 109799877Syar struct group *grp; 109899877Syar 1099109893Syar if (p[1] == '\0') /* single @ matches anyone */ 110099877Syar found = 1; 1101109893Syar else { 1102109893Syar if ((grp = getgrnam(p+1)) == NULL) 1103109893Syar goto nextline; 1104109893Syar /* 1105109893Syar * Check user's default group 1106109893Syar */ 1107109893Syar if (pwset && grp->gr_gid == pw->pw_gid) 1108109893Syar found = 1; 1109109893Syar /* 1110109893Syar * Check supplementary groups 1111109893Syar */ 1112109893Syar while (!found && grp->gr_mem[i]) 1113109893Syar found = strcmp(name, 1114109893Syar grp->gr_mem[i++]) 1115109893Syar == 0; 1116109893Syar } 11171592Srgrimes } 111899877Syar /* 111999877Syar * Otherwise, just check for username match 112099877Syar */ 112199877Syar else 112299877Syar found = strcmp(p, name) == 0; 1123109893Syar /* 1124109893Syar * Save the rest of line to "residue" if matched 1125109893Syar */ 1126109893Syar if (found && residue) { 1127109938Syar if ((p = strtok(NULL, "")) != NULL) 1128109938Syar p += strspn(p, " \t"); 1129109938Syar if (p && *p) { 1130109893Syar if ((*residue = strdup(p)) == NULL) 1131109893Syar fatalerror("Ran out of memory."); 1132109893Syar } else 1133109893Syar *residue = NULL; 1134109893Syar } 113599877Syarnextline: 113699877Syar if (mp) 113799877Syar free(mp); 113899877Syar } 11391592Srgrimes (void) fclose(fd); 11401592Srgrimes } 11411592Srgrimes return (found); 11421592Srgrimes} 11431592Srgrimes 11441592Srgrimes/* 11451592Srgrimes * Terminate login as previous user, if any, resetting state; 11461592Srgrimes * used when USER command is given or login fails. 11471592Srgrimes */ 11481592Srgrimesstatic void 114990148Simpend_login(void) 11501592Srgrimes{ 115174874Smarkm#ifdef USE_PAM 115274874Smarkm int e; 115374874Smarkm#endif 11541592Srgrimes 11551592Srgrimes (void) seteuid((uid_t)0); 1156102311Syar if (logged_in && dowtmp) 115789920Sume ftpd_logwtmp(ttyline, "", NULL); 11581592Srgrimes pw = NULL; 115925101Sdavidn#ifdef LOGIN_CAP 116025101Sdavidn setusercontext(NULL, getpwuid(0), (uid_t)0, 1161105877Srwatson LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK| 1162105877Srwatson LOGIN_SETMAC); 116325101Sdavidn#endif 116474874Smarkm#ifdef USE_PAM 116574874Smarkm if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 116674874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 116774874Smarkm if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 116874874Smarkm syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 116974874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 117074874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 117174874Smarkm pamh = NULL; 117274874Smarkm#endif 11731592Srgrimes logged_in = 0; 11741592Srgrimes guest = 0; 117517435Spst dochroot = 0; 11761592Srgrimes} 11771592Srgrimes 117874874Smarkm#ifdef USE_PAM 117951433Smarkm 118051433Smarkm/* 118151433Smarkm * the following code is stolen from imap-uw PAM authentication module and 118251433Smarkm * login.c 118351433Smarkm */ 118451433Smarkm#define COPY_STRING(s) (s ? strdup(s) : NULL) 118551433Smarkm 118651433Smarkmstruct cred_t { 118751433Smarkm const char *uname; /* user name */ 118851433Smarkm const char *pass; /* password */ 118951433Smarkm}; 119051433Smarkmtypedef struct cred_t cred_t; 119151433Smarkm 119251433Smarkmstatic int 119351433Smarkmauth_conv(int num_msg, const struct pam_message **msg, 119451433Smarkm struct pam_response **resp, void *appdata) 119551433Smarkm{ 119651433Smarkm int i; 119751433Smarkm cred_t *cred = (cred_t *) appdata; 119891244Sdes struct pam_response *reply; 119951433Smarkm 120091244Sdes reply = calloc(num_msg, sizeof *reply); 120191244Sdes if (reply == NULL) 120291244Sdes return PAM_BUF_ERR; 120391244Sdes 120451433Smarkm for (i = 0; i < num_msg; i++) { 120551433Smarkm switch (msg[i]->msg_style) { 120651433Smarkm case PAM_PROMPT_ECHO_ON: /* assume want user name */ 120751433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 120851433Smarkm reply[i].resp = COPY_STRING(cred->uname); 120951433Smarkm /* PAM frees resp. */ 121051433Smarkm break; 121151433Smarkm case PAM_PROMPT_ECHO_OFF: /* assume want password */ 121251433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 121351433Smarkm reply[i].resp = COPY_STRING(cred->pass); 121451433Smarkm /* PAM frees resp. */ 121551433Smarkm break; 121651433Smarkm case PAM_TEXT_INFO: 121751433Smarkm case PAM_ERROR_MSG: 121851433Smarkm reply[i].resp_retcode = PAM_SUCCESS; 121951433Smarkm reply[i].resp = NULL; 122051433Smarkm break; 122151433Smarkm default: /* unknown message style */ 122251433Smarkm free(reply); 122351433Smarkm return PAM_CONV_ERR; 122451433Smarkm } 122551433Smarkm } 122651433Smarkm 122751433Smarkm *resp = reply; 122851433Smarkm return PAM_SUCCESS; 122951433Smarkm} 123051433Smarkm 123151433Smarkm/* 123251433Smarkm * Attempt to authenticate the user using PAM. Returns 0 if the user is 123351433Smarkm * authenticated, or 1 if not authenticated. If some sort of PAM system 123451433Smarkm * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 123551433Smarkm * function returns -1. This can be used as an indication that we should 123651433Smarkm * fall back to a different authentication mechanism. 123751433Smarkm */ 123851433Smarkmstatic int 123951433Smarkmauth_pam(struct passwd **ppw, const char *pass) 124051433Smarkm{ 124151433Smarkm pam_handle_t *pamh = NULL; 124251433Smarkm const char *tmpl_user; 124351433Smarkm const void *item; 124451433Smarkm int rval; 124551433Smarkm int e; 124651433Smarkm cred_t auth_cred = { (*ppw)->pw_name, pass }; 124751433Smarkm struct pam_conv conv = { &auth_conv, &auth_cred }; 124851433Smarkm 124951433Smarkm e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 125051433Smarkm if (e != PAM_SUCCESS) { 125151433Smarkm syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); 125251433Smarkm return -1; 125351433Smarkm } 125451433Smarkm 125567007Sguido e = pam_set_item(pamh, PAM_RHOST, remotehost); 125667007Sguido if (e != PAM_SUCCESS) { 125767007Sguido syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 125867007Sguido pam_strerror(pamh, e)); 125967007Sguido return -1; 126067007Sguido } 126167007Sguido 126251433Smarkm e = pam_authenticate(pamh, 0); 126351433Smarkm switch (e) { 126451433Smarkm case PAM_SUCCESS: 126551433Smarkm /* 126651433Smarkm * With PAM we support the concept of a "template" 126751433Smarkm * user. The user enters a login name which is 126851433Smarkm * authenticated by PAM, usually via a remote service 126951433Smarkm * such as RADIUS or TACACS+. If authentication 127051433Smarkm * succeeds, a different but related "template" name 127151433Smarkm * is used for setting the credentials, shell, and 127251433Smarkm * home directory. The name the user enters need only 127351433Smarkm * exist on the remote authentication server, but the 127451433Smarkm * template name must be present in the local password 127551433Smarkm * database. 127651433Smarkm * 127751433Smarkm * This is supported by two various mechanisms in the 127851433Smarkm * individual modules. However, from the application's 127951433Smarkm * point of view, the template user is always passed 128051433Smarkm * back as a changed value of the PAM_USER item. 128151433Smarkm */ 128251433Smarkm if ((e = pam_get_item(pamh, PAM_USER, &item)) == 128351433Smarkm PAM_SUCCESS) { 128451433Smarkm tmpl_user = (const char *) item; 128551433Smarkm if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 128651433Smarkm *ppw = getpwnam(tmpl_user); 128751433Smarkm } else 128851433Smarkm syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 128951433Smarkm pam_strerror(pamh, e)); 129051433Smarkm rval = 0; 129151433Smarkm break; 129251433Smarkm 129351433Smarkm case PAM_AUTH_ERR: 129451433Smarkm case PAM_USER_UNKNOWN: 129551433Smarkm case PAM_MAXTRIES: 129651433Smarkm rval = 1; 129751433Smarkm break; 129851433Smarkm 129951433Smarkm default: 130074874Smarkm syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 130151433Smarkm rval = -1; 130251433Smarkm break; 130351433Smarkm } 130451433Smarkm 130574874Smarkm if (rval == 0) { 130674874Smarkm e = pam_acct_mgmt(pamh, 0); 130774874Smarkm if (e == PAM_NEW_AUTHTOK_REQD) { 130874874Smarkm e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 130974874Smarkm if (e != PAM_SUCCESS) { 131074874Smarkm syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e)); 131174874Smarkm rval = 1; 131274874Smarkm } 131374874Smarkm } else if (e != PAM_SUCCESS) { 131474874Smarkm rval = 1; 131574874Smarkm } 131651433Smarkm } 131774874Smarkm 131874874Smarkm if (rval != 0) { 131974874Smarkm if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 132074874Smarkm syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 132174874Smarkm } 132274874Smarkm pamh = NULL; 132374874Smarkm } 132451433Smarkm return rval; 132551433Smarkm} 132651433Smarkm 132774874Smarkm#endif /* USE_PAM */ 132851433Smarkm 13291592Srgrimesvoid 133090148Simppass(char *passwd) 13311592Srgrimes{ 133217435Spst int rval; 13331592Srgrimes FILE *fd; 133425101Sdavidn#ifdef LOGIN_CAP 133525101Sdavidn login_cap_t *lc = NULL; 133625101Sdavidn#endif 133774874Smarkm#ifdef USE_PAM 133874874Smarkm int e; 133974874Smarkm#endif 1340110036Syar char *chrootdir; 1341109939Syar char *residue = NULL; 134288763Sache char *xpasswd; 13431592Srgrimes 13441592Srgrimes if (logged_in || askpasswd == 0) { 13451592Srgrimes reply(503, "Login with USER first."); 13461592Srgrimes return; 13471592Srgrimes } 13481592Srgrimes askpasswd = 0; 13491592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 135017435Spst if (pw == NULL) { 135117435Spst rval = 1; /* failure below */ 135217435Spst goto skip; 135317435Spst } 135474874Smarkm#ifdef USE_PAM 135551433Smarkm rval = auth_pam(&pw, passwd); 135689622Sache if (rval >= 0) { 135789622Sache opieunlock(); 135817435Spst goto skip; 135989622Sache } 136089622Sache#endif 136188763Sache if (opieverify(&opiedata, passwd) == 0) 136288763Sache xpasswd = pw->pw_passwd; 136389622Sache else if (pwok) { 136488763Sache xpasswd = crypt(passwd, pw->pw_passwd); 136589622Sache if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 136689622Sache xpasswd = ":"; 136789622Sache } else { 136888763Sache rval = 1; 136988763Sache goto skip; 137088763Sache } 137188763Sache rval = strcmp(pw->pw_passwd, xpasswd); 137289622Sache if (pw->pw_expire && time(NULL) >= pw->pw_expire) 137317435Spst rval = 1; /* failure */ 137417435Spstskip: 137517435Spst /* 137617435Spst * If rval == 1, the user failed the authentication check 137751433Smarkm * above. If rval == 0, either PAM or local authentication 137817435Spst * succeeded. 137917435Spst */ 138017435Spst if (rval) { 13811592Srgrimes reply(530, "Login incorrect."); 1382110691Syar if (logging) { 13831592Srgrimes syslog(LOG_NOTICE, 1384110691Syar "FTP LOGIN FAILED FROM %s", 1385110691Syar remotehost); 1386110691Syar syslog(LOG_AUTHPRIV | LOG_NOTICE, 13871592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 13881592Srgrimes remotehost, curname); 1389110691Syar } 13901592Srgrimes pw = NULL; 13911592Srgrimes if (login_attempts++ >= 5) { 13921592Srgrimes syslog(LOG_NOTICE, 13931592Srgrimes "repeated login failures from %s", 13941592Srgrimes remotehost); 13951592Srgrimes exit(0); 13961592Srgrimes } 13971592Srgrimes return; 13981592Srgrimes } 13991592Srgrimes } 14001592Srgrimes login_attempts = 0; /* this time successful */ 14011592Srgrimes if (setegid((gid_t)pw->pw_gid) < 0) { 14021592Srgrimes reply(550, "Can't set gid."); 14031592Srgrimes return; 14041592Srgrimes } 140525101Sdavidn /* May be overridden by login.conf */ 140625101Sdavidn (void) umask(defumask); 140725101Sdavidn#ifdef LOGIN_CAP 140825674Sdavidn if ((lc = login_getpwclass(pw)) != NULL) { 140925101Sdavidn char remote_ip[MAXHOSTNAMELEN]; 141025101Sdavidn 141156668Sshin getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 141256668Sshin remote_ip, sizeof(remote_ip) - 1, NULL, 0, 141399255Sume NI_NUMERICHOST); 141425101Sdavidn remote_ip[sizeof(remote_ip) - 1] = 0; 141525101Sdavidn if (!auth_hostok(lc, remotehost, remote_ip)) { 141625101Sdavidn syslog(LOG_INFO|LOG_AUTH, 141725101Sdavidn "FTP LOGIN FAILED (HOST) as %s: permission denied.", 141825101Sdavidn pw->pw_name); 141925101Sdavidn reply(530, "Permission denied.\n"); 142025101Sdavidn pw = NULL; 142125101Sdavidn return; 142225101Sdavidn } 142325101Sdavidn if (!auth_timeok(lc, time(NULL))) { 142425101Sdavidn reply(530, "Login not available right now.\n"); 142525101Sdavidn pw = NULL; 142625101Sdavidn return; 142725101Sdavidn } 142825101Sdavidn } 142925101Sdavidn setusercontext(lc, pw, (uid_t)0, 143040310Sdes LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| 1431105877Srwatson LOGIN_SETRESOURCES|LOGIN_SETUMASK|LOGIN_SETMAC); 143225101Sdavidn#else 143340310Sdes setlogin(pw->pw_name); 14341592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 143525101Sdavidn#endif 14361592Srgrimes 143774874Smarkm#ifdef USE_PAM 143874874Smarkm if (pamh) { 143974874Smarkm if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 144074874Smarkm syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 144174874Smarkm } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 144274874Smarkm syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 144374874Smarkm } 144474874Smarkm } 144574874Smarkm#endif 144674874Smarkm 14471592Srgrimes /* open wtmp before chroot */ 1448102311Syar if (dowtmp) 1449102311Syar ftpd_logwtmp(ttyline, pw->pw_name, 1450102311Syar (struct sockaddr *)&his_addr); 14511592Srgrimes logged_in = 1; 14521592Srgrimes 145317435Spst if (guest && stats && statfd < 0) 145425283Sdavidn#ifdef VIRTUAL_HOSTING 145525283Sdavidn if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) 145625283Sdavidn#else 14576740Sguido if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) 145825283Sdavidn#endif 14596740Sguido stats = 0; 14606740Sguido 146125101Sdavidn dochroot = 1462109939Syar checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue) 146325101Sdavidn#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 1464109893Syar || login_getcapbool(lc, "ftp-chroot", 0) 146525101Sdavidn#endif 1466109893Syar ; 1467110036Syar chrootdir = NULL; 1468110036Syar /* 1469110036Syar * For a chrooted local user, 1470110036Syar * a) see whether ftpchroot(5) specifies a chroot directory, 1471110036Syar * b) extract the directory pathname from the line, 1472110036Syar * c) expand it to the absolute pathname if necessary. 1473110036Syar */ 1474110036Syar if (dochroot && residue && 1475117349Syar (chrootdir = strtok(residue, " \t")) != NULL) { 1476117349Syar if (chrootdir[0] != '/') 1477117349Syar asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); 1478117349Syar else 1479117349Syar chrootdir = strdup(chrootdir); /* so it can be freed */ 1480110036Syar if (chrootdir == NULL) 1481110036Syar fatalerror("Ran out of memory."); 1482110036Syar } 1483110036Syar if (guest || dochroot) { 14841592Srgrimes /* 1485110036Syar * If no chroot directory set yet, use the login directory. 1486110036Syar * Copy it so it can be modified while pw->pw_dir stays intact. 14871592Srgrimes */ 1488110036Syar if (chrootdir == NULL && 1489110036Syar (chrootdir = strdup(pw->pw_dir)) == NULL) 1490110036Syar fatalerror("Ran out of memory."); 1491110036Syar /* 1492110036Syar * Check for the "/chroot/./home" syntax, 1493110036Syar * separate the chroot and home directory pathnames. 1494110036Syar */ 1495110036Syar if ((homedir = strstr(chrootdir, "/./")) != NULL) { 1496110036Syar *(homedir++) = '\0'; /* wipe '/' */ 1497110036Syar homedir++; /* skip '.' */ 1498110036Syar /* so chrootdir can be freed later */ 1499110036Syar if ((homedir = strdup(homedir)) == NULL) 1500109893Syar fatalerror("Ran out of memory."); 1501110036Syar } else { 1502110036Syar /* 1503110036Syar * We MUST do a chdir() after the chroot. Otherwise 1504110036Syar * the old current directory will be accessible as "." 1505110036Syar * outside the new root! 1506110036Syar */ 1507110036Syar homedir = "/"; 1508109939Syar } 1509110036Syar /* 1510110036Syar * Finally, do chroot() 1511110036Syar */ 1512110036Syar if (chroot(chrootdir) < 0) { 151317435Spst reply(550, "Can't change root."); 151417435Spst goto bad; 151517435Spst } 1516110036Syar } else /* real user w/o chroot */ 1517110036Syar homedir = pw->pw_dir; 1518110036Syar /* 1519110036Syar * Set euid *before* doing chdir() so 1520110036Syar * a) the user won't be carried to a directory that he couldn't reach 1521110036Syar * on his own due to no permission to upper path components, 1522110036Syar * b) NFS mounted homedirs w/restrictive permissions will be accessible 1523110036Syar * (uid 0 has no root power over NFS if not mapped explicitly.) 1524110036Syar */ 15251592Srgrimes if (seteuid((uid_t)pw->pw_uid) < 0) { 15261592Srgrimes reply(550, "Can't set uid."); 15271592Srgrimes goto bad; 15281592Srgrimes } 1529110036Syar if (chdir(homedir) < 0) { 1530110036Syar if (guest || dochroot) { 1531110036Syar reply(550, "Can't change to base directory."); 1532110036Syar goto bad; 1533110036Syar } else { 1534110036Syar if (chdir("/") < 0) { 1535110036Syar reply(550, "Root is inaccessible."); 1536110036Syar goto bad; 1537110036Syar } 1538110036Syar lreply(230, "No directory! Logging in with home=/"); 1539110036Syar } 1540110036Syar } 15418696Sdg 15421592Srgrimes /* 15431592Srgrimes * Display a login message, if it exists. 15441592Srgrimes * N.B. reply(230,) must follow the message. 15451592Srgrimes */ 154625283Sdavidn#ifdef VIRTUAL_HOSTING 154725283Sdavidn if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { 154825283Sdavidn#else 15491592Srgrimes if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 155025283Sdavidn#endif 15511592Srgrimes char *cp, line[LINE_MAX]; 15521592Srgrimes 15531592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 15541592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 15551592Srgrimes *cp = '\0'; 15561592Srgrimes lreply(230, "%s", line); 15571592Srgrimes } 15581592Srgrimes (void) fflush(stdout); 15591592Srgrimes (void) fclose(fd); 15601592Srgrimes } 15611592Srgrimes if (guest) { 15626740Sguido if (ident != NULL) 15636740Sguido free(ident); 156417433Spst ident = strdup(passwd); 156517433Spst if (ident == NULL) 156676096Smarkm fatalerror("Ran out of memory."); 156717433Spst 15681592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 15691592Srgrimes#ifdef SETPROCTITLE 157025283Sdavidn#ifdef VIRTUAL_HOSTING 157125283Sdavidn if (thishost != firsthost) 157225283Sdavidn snprintf(proctitle, sizeof(proctitle), 157383308Smikeh "%s: anonymous(%s)/%s", remotehost, hostname, 157483308Smikeh passwd); 157525283Sdavidn else 157625283Sdavidn#endif 157725283Sdavidn snprintf(proctitle, sizeof(proctitle), 157883308Smikeh "%s: anonymous/%s", remotehost, passwd); 157913139Speter setproctitle("%s", proctitle); 15801592Srgrimes#endif /* SETPROCTITLE */ 15811592Srgrimes if (logging) 15821592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 15831592Srgrimes remotehost, passwd); 15841592Srgrimes } else { 158584841Syar if (dochroot) 158684841Syar reply(230, "User %s logged in, " 158784841Syar "access restrictions apply.", pw->pw_name); 158884841Syar else 158984841Syar reply(230, "User %s logged in.", pw->pw_name); 159025986Sdanny 15911592Srgrimes#ifdef SETPROCTITLE 15921592Srgrimes snprintf(proctitle, sizeof(proctitle), 159384842Syar "%s: user/%s", remotehost, pw->pw_name); 159413139Speter setproctitle("%s", proctitle); 15951592Srgrimes#endif /* SETPROCTITLE */ 15961592Srgrimes if (logging) 15971592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 15981592Srgrimes remotehost, pw->pw_name); 15991592Srgrimes } 160025101Sdavidn#ifdef LOGIN_CAP 160125101Sdavidn login_close(lc); 160225101Sdavidn#endif 1603110036Syar if (chrootdir) 1604110036Syar free(chrootdir); 1605110036Syar if (residue) 1606110036Syar free(residue); 16071592Srgrimes return; 16081592Srgrimesbad: 16091592Srgrimes /* Forget all about it... */ 161025101Sdavidn#ifdef LOGIN_CAP 161125101Sdavidn login_close(lc); 161225101Sdavidn#endif 1613110036Syar if (chrootdir) 1614110036Syar free(chrootdir); 1615110036Syar if (residue) 1616110036Syar free(residue); 16171592Srgrimes end_login(); 16181592Srgrimes} 16191592Srgrimes 16201592Srgrimesvoid 162190148Simpretrieve(char *cmd, char *name) 16221592Srgrimes{ 16231592Srgrimes FILE *fin, *dout; 16241592Srgrimes struct stat st; 162590148Simp int (*closefunc)(FILE *); 162636612Sjb time_t start; 16271592Srgrimes 16281592Srgrimes if (cmd == 0) { 16291592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 16301592Srgrimes st.st_size = 0; 16311592Srgrimes } else { 16321592Srgrimes char line[BUFSIZ]; 16331592Srgrimes 163431973Simp (void) snprintf(line, sizeof(line), cmd, name), name = line; 16351592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 16361592Srgrimes st.st_size = -1; 16371592Srgrimes st.st_blksize = BUFSIZ; 16381592Srgrimes } 16391592Srgrimes if (fin == NULL) { 16401592Srgrimes if (errno != 0) { 16411592Srgrimes perror_reply(550, name); 16421592Srgrimes if (cmd == 0) { 16431592Srgrimes LOGCMD("get", name); 16441592Srgrimes } 16451592Srgrimes } 16461592Srgrimes return; 16471592Srgrimes } 16481592Srgrimes byte_count = -1; 1649110144Syar if (cmd == 0) { 1650110144Syar if (fstat(fileno(fin), &st) < 0) { 1651110144Syar perror_reply(550, name); 1652110144Syar goto done; 1653110144Syar } 1654110144Syar if (!S_ISREG(st.st_mode)) { 1655110144Syar if (guest) { 1656110144Syar reply(550, "%s: not a plain file.", name); 1657110144Syar goto done; 1658110144Syar } 1659110144Syar st.st_size = -1; 1660110144Syar /* st.st_blksize is set for all descriptor types */ 1661110144Syar } 16621592Srgrimes } 16631592Srgrimes if (restart_point) { 16641592Srgrimes if (type == TYPE_A) { 16651592Srgrimes off_t i, n; 16661592Srgrimes int c; 16671592Srgrimes 16681592Srgrimes n = restart_point; 16691592Srgrimes i = 0; 16701592Srgrimes while (i++ < n) { 16711592Srgrimes if ((c=getc(fin)) == EOF) { 16721592Srgrimes perror_reply(550, name); 16731592Srgrimes goto done; 16741592Srgrimes } 16751592Srgrimes if (c == '\n') 16761592Srgrimes i++; 16771592Srgrimes } 16781592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 16791592Srgrimes perror_reply(550, name); 16801592Srgrimes goto done; 16811592Srgrimes } 16821592Srgrimes } 16831592Srgrimes dout = dataconn(name, st.st_size, "w"); 16841592Srgrimes if (dout == NULL) 16851592Srgrimes goto done; 16866740Sguido time(&start); 16878240Swollman send_data(fin, dout, st.st_blksize, st.st_size, 16888240Swollman restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 16896740Sguido if (cmd == 0 && guest && stats) 169017433Spst logxfer(name, st.st_size, start); 16911592Srgrimes (void) fclose(dout); 16921592Srgrimes data = -1; 16931592Srgrimes pdata = -1; 16941592Srgrimesdone: 16951592Srgrimes if (cmd == 0) 16961592Srgrimes LOGBYTES("get", name, byte_count); 16971592Srgrimes (*closefunc)(fin); 16981592Srgrimes} 16991592Srgrimes 17001592Srgrimesvoid 170190148Simpstore(char *name, char *mode, int unique) 17021592Srgrimes{ 1703101537Syar int fd; 17041592Srgrimes FILE *fout, *din; 170590148Simp int (*closefunc)(FILE *); 17061592Srgrimes 1707101537Syar if (*mode == 'a') { /* APPE */ 1708101537Syar if (unique) { 1709101537Syar /* Programming error */ 1710101537Syar syslog(LOG_ERR, "Internal: unique flag to APPE"); 1711101537Syar unique = 0; 1712101537Syar } 1713101537Syar if (guest && noguestmod) { 1714101537Syar reply(550, "Appending to existing file denied"); 1715101537Syar goto err; 1716101537Syar } 1717101537Syar restart_point = 0; /* not affected by preceding REST */ 17181592Srgrimes } 1719101537Syar if (unique) /* STOU overrides REST */ 1720101537Syar restart_point = 0; 1721101537Syar if (guest && noguestmod) { 1722101537Syar if (restart_point) { /* guest STOR w/REST */ 1723101537Syar reply(550, "Modifying existing file denied"); 1724101537Syar goto err; 1725101537Syar } else /* treat guest STOR as STOU */ 1726101537Syar unique = 1; 1727101537Syar } 17281592Srgrimes 17291592Srgrimes if (restart_point) 1730101537Syar mode = "r+"; /* so ASCII manual seek can work */ 1731101537Syar if (unique) { 1732101537Syar if ((fd = guniquefd(name, &name)) < 0) 1733101537Syar goto err; 1734101537Syar fout = fdopen(fd, mode); 1735101537Syar } else 1736101537Syar fout = fopen(name, mode); 17371592Srgrimes closefunc = fclose; 17381592Srgrimes if (fout == NULL) { 17391592Srgrimes perror_reply(553, name); 1740101537Syar goto err; 17411592Srgrimes } 17421592Srgrimes byte_count = -1; 17431592Srgrimes if (restart_point) { 17441592Srgrimes if (type == TYPE_A) { 17451592Srgrimes off_t i, n; 17461592Srgrimes int c; 17471592Srgrimes 17481592Srgrimes n = restart_point; 17491592Srgrimes i = 0; 17501592Srgrimes while (i++ < n) { 17511592Srgrimes if ((c=getc(fout)) == EOF) { 17521592Srgrimes perror_reply(550, name); 17531592Srgrimes goto done; 17541592Srgrimes } 17551592Srgrimes if (c == '\n') 17561592Srgrimes i++; 17571592Srgrimes } 17581592Srgrimes /* 17591592Srgrimes * We must do this seek to "current" position 17601592Srgrimes * because we are changing from reading to 17611592Srgrimes * writing. 17621592Srgrimes */ 176382792Sache if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { 17641592Srgrimes perror_reply(550, name); 17651592Srgrimes goto done; 17661592Srgrimes } 17671592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 17681592Srgrimes perror_reply(550, name); 17691592Srgrimes goto done; 17701592Srgrimes } 17711592Srgrimes } 17721592Srgrimes din = dataconn(name, (off_t)-1, "r"); 17731592Srgrimes if (din == NULL) 17741592Srgrimes goto done; 17751592Srgrimes if (receive_data(din, fout) == 0) { 17761592Srgrimes if (unique) 17771592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 17781592Srgrimes name); 17791592Srgrimes else 17801592Srgrimes reply(226, "Transfer complete."); 17811592Srgrimes } 17821592Srgrimes (void) fclose(din); 17831592Srgrimes data = -1; 17841592Srgrimes pdata = -1; 17851592Srgrimesdone: 1786102566Syar LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); 17871592Srgrimes (*closefunc)(fout); 1788101537Syar return; 1789101537Syarerr: 1790101537Syar LOGCMD(*mode == 'a' ? "append" : "put" , name); 1791101537Syar return; 17921592Srgrimes} 17931592Srgrimes 17941592Srgrimesstatic FILE * 179590148Simpgetdatasock(char *mode) 17961592Srgrimes{ 17971592Srgrimes int on = 1, s, t, tries; 17981592Srgrimes 17991592Srgrimes if (data >= 0) 18001592Srgrimes return (fdopen(data, mode)); 18011592Srgrimes (void) seteuid((uid_t)0); 180256668Sshin 180356668Sshin s = socket(data_dest.su_family, SOCK_STREAM, 0); 18041592Srgrimes if (s < 0) 18051592Srgrimes goto bad; 1806100612Syar if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1807100609Syar syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 18081592Srgrimes /* anchor socket to avoid multi-homing problems */ 180956668Sshin data_source = ctrl_addr; 1810109742Syar data_source.su_port = htons(dataport); 18111592Srgrimes for (tries = 1; ; tries++) { 18121592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 181356668Sshin data_source.su_len) >= 0) 18141592Srgrimes break; 18151592Srgrimes if (errno != EADDRINUSE || tries > 10) 18161592Srgrimes goto bad; 18171592Srgrimes sleep(tries); 18181592Srgrimes } 18191592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 18201592Srgrimes#ifdef IP_TOS 182156668Sshin if (data_source.su_family == AF_INET) 182256668Sshin { 18231592Srgrimes on = IPTOS_THROUGHPUT; 1824100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 1825100609Syar syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 182656668Sshin } 18271592Srgrimes#endif 18288240Swollman#ifdef TCP_NOPUSH 18298240Swollman /* 18308240Swollman * Turn off push flag to keep sender TCP from sending short packets 18318240Swollman * at the boundaries of each write(). Should probably do a SO_SNDBUF 18328240Swollman * to set the send buffer size as well, but that may not be desirable 18338240Swollman * in heavy-load situations. 18348240Swollman */ 18358240Swollman on = 1; 1836100612Syar if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 1837100609Syar syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 18388240Swollman#endif 18398240Swollman#ifdef SO_SNDBUF 18408240Swollman on = 65536; 1841100612Syar if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof on) < 0) 1842100609Syar syslog(LOG_WARNING, "data setsockopt (SO_SNDBUF): %m"); 18438240Swollman#endif 18448240Swollman 18451592Srgrimes return (fdopen(s, mode)); 18461592Srgrimesbad: 18471592Srgrimes /* Return the real value of errno (close may change it) */ 18481592Srgrimes t = errno; 18491592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 18501592Srgrimes (void) close(s); 18511592Srgrimes errno = t; 18521592Srgrimes return (NULL); 18531592Srgrimes} 18541592Srgrimes 18551592Srgrimesstatic FILE * 185690148Simpdataconn(char *name, off_t size, char *mode) 18571592Srgrimes{ 18581592Srgrimes char sizebuf[32]; 18591592Srgrimes FILE *file; 1860109611Scjc int retry = 0, tos, conerrno; 18611592Srgrimes 18621592Srgrimes file_size = size; 18631592Srgrimes byte_count = 0; 18641592Srgrimes if (size != (off_t) -1) 186531973Simp (void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size); 18661592Srgrimes else 186731973Simp *sizebuf = '\0'; 18681592Srgrimes if (pdata >= 0) { 186956668Sshin union sockunion from; 187086628Syar int flags; 187156668Sshin int s, fromlen = ctrl_addr.su_len; 187212532Sguido struct timeval timeout; 187312532Sguido fd_set set; 18741592Srgrimes 187512532Sguido FD_ZERO(&set); 187612532Sguido FD_SET(pdata, &set); 187712532Sguido 187812532Sguido timeout.tv_usec = 0; 187912532Sguido timeout.tv_sec = 120; 188012532Sguido 188186628Syar /* 188286628Syar * Granted a socket is in the blocking I/O mode, 188386628Syar * accept() will block after a successful select() 188486628Syar * if the selected connection dies in between. 188586628Syar * Therefore set the non-blocking I/O flag here. 188686628Syar */ 188786628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 188886628Syar fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 188986628Syar goto pdata_err; 189086628Syar if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) <= 0 || 189186628Syar (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 189286628Syar goto pdata_err; 18931592Srgrimes (void) close(pdata); 18941592Srgrimes pdata = s; 189586628Syar /* 1896101809Syar * Unset the inherited non-blocking I/O flag 1897101809Syar * on the child socket so stdio can work on it. 189886628Syar */ 189986628Syar if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 190086628Syar fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 190186628Syar goto pdata_err; 19021592Srgrimes#ifdef IP_TOS 190356668Sshin if (from.su_family == AF_INET) 190456668Sshin { 190517435Spst tos = IPTOS_THROUGHPUT; 1906100612Syar if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 1907100609Syar syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 190856668Sshin } 19091592Srgrimes#endif 19101592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19111592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19121592Srgrimes return (fdopen(pdata, mode)); 191386628Syarpdata_err: 191486628Syar reply(425, "Can't open data connection."); 191586628Syar (void) close(pdata); 191686628Syar pdata = -1; 191786628Syar return (NULL); 19181592Srgrimes } 19191592Srgrimes if (data >= 0) { 19201592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 19211592Srgrimes name, sizebuf); 19221592Srgrimes usedefault = 1; 19231592Srgrimes return (fdopen(data, mode)); 19241592Srgrimes } 19251592Srgrimes if (usedefault) 19261592Srgrimes data_dest = his_addr; 19271592Srgrimes usedefault = 1; 1928109611Scjc do { 1929109611Scjc file = getdatasock(mode); 1930109611Scjc if (file == NULL) { 1931109611Scjc char hostbuf[BUFSIZ], portbuf[BUFSIZ]; 1932109611Scjc getnameinfo((struct sockaddr *)&data_source, 1933109611Scjc data_source.su_len, hostbuf, sizeof(hostbuf) - 1, 1934109611Scjc portbuf, sizeof(portbuf), 1935109611Scjc NI_NUMERICHOST|NI_NUMERICSERV); 1936109611Scjc reply(425, "Can't create data socket (%s,%s): %s.", 1937109611Scjc hostbuf, portbuf, strerror(errno)); 1938109611Scjc return (NULL); 1939109611Scjc } 1940109611Scjc data = fileno(file); 1941109611Scjc conerrno = 0; 1942109611Scjc if (connect(data, (struct sockaddr *)&data_dest, 1943109611Scjc data_dest.su_len) == 0) 1944109611Scjc break; 1945109611Scjc conerrno = errno; 1946109611Scjc (void) fclose(file); 1947109611Scjc data = -1; 1948109611Scjc if (conerrno == EADDRINUSE) { 19491592Srgrimes sleep((unsigned) swaitint); 19501592Srgrimes retry += swaitint; 1951109611Scjc } else { 1952109611Scjc break; 19531592Srgrimes } 1954109611Scjc } while (retry <= swaitmax); 1955109611Scjc if (conerrno != 0) { 19561592Srgrimes perror_reply(425, "Can't build data connection"); 19571592Srgrimes return (NULL); 19581592Srgrimes } 19591592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 19601592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 19611592Srgrimes return (file); 19621592Srgrimes} 19631592Srgrimes 19641592Srgrimes/* 19651592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate 19668240Swollman * encapsulation of the data subject to Mode, Structure, and Type. 19671592Srgrimes * 19681592Srgrimes * NB: Form isn't handled. 19691592Srgrimes */ 197089935Syarstatic int 197190148Simpsend_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg) 19721592Srgrimes{ 1973122751Syar int c, cp, filefd, netfd; 197470205Sdan char *buf; 197570205Sdan off_t cnt; 19761592Srgrimes 19771592Srgrimes transflag++; 19781592Srgrimes switch (type) { 19791592Srgrimes 19801592Srgrimes case TYPE_A: 1981122751Syar cp = '\0'; 19821592Srgrimes while ((c = getc(instr)) != EOF) { 198389935Syar if (recvurg) 198489935Syar goto got_oob; 19851592Srgrimes byte_count++; 1986122751Syar if (c == '\n' && cp != '\r') { 19871592Srgrimes if (ferror(outstr)) 19881592Srgrimes goto data_err; 19891592Srgrimes (void) putc('\r', outstr); 19901592Srgrimes } 19911592Srgrimes (void) putc(c, outstr); 1992122751Syar cp = c; 19931592Srgrimes } 199489935Syar if (recvurg) 199589935Syar goto got_oob; 19961592Srgrimes fflush(outstr); 19971592Srgrimes transflag = 0; 19981592Srgrimes if (ferror(instr)) 19991592Srgrimes goto file_err; 20001592Srgrimes if (ferror(outstr)) 20011592Srgrimes goto data_err; 20021592Srgrimes reply(226, "Transfer complete."); 200389935Syar return (0); 20041592Srgrimes 20051592Srgrimes case TYPE_I: 20061592Srgrimes case TYPE_L: 20078240Swollman /* 20088240Swollman * isreg is only set if we are not doing restart and we 20098240Swollman * are sending a regular file 20108240Swollman */ 20118240Swollman netfd = fileno(outstr); 20128870Srgrimes filefd = fileno(instr); 20138240Swollman 201470205Sdan if (isreg) { 201570205Sdan 201670205Sdan off_t offset; 201770205Sdan int err; 201870205Sdan 201970205Sdan err = cnt = offset = 0; 202070205Sdan 202190604Smaxim while (err != -1 && filesize > 0) { 202290604Smaxim err = sendfile(filefd, netfd, offset, 0, 202370205Sdan (struct sf_hdtr *) NULL, &cnt, 0); 202499212Smaxim /* 202599212Smaxim * Calculate byte_count before OOB processing. 202699212Smaxim * It can be used in myoob() later. 202799212Smaxim */ 202899212Smaxim byte_count += cnt; 202989935Syar if (recvurg) 203089935Syar goto got_oob; 203170205Sdan offset += cnt; 203290604Smaxim filesize -= cnt; 20338240Swollman 203470205Sdan if (err == -1) { 203570205Sdan if (!cnt) 203670205Sdan goto oldway; 203770205Sdan 203870205Sdan goto data_err; 203970205Sdan } 204070205Sdan } 204170205Sdan 204299318Sdan transflag = 0; 20438240Swollman reply(226, "Transfer complete."); 204489935Syar return (0); 20458240Swollman } 20468240Swollman 20478240Swollmanoldway: 20481592Srgrimes if ((buf = malloc((u_int)blksize)) == NULL) { 20491592Srgrimes transflag = 0; 20501592Srgrimes perror_reply(451, "Local resource failure: malloc"); 205189935Syar return (-1); 20521592Srgrimes } 20538870Srgrimes 20541592Srgrimes while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 20551592Srgrimes write(netfd, buf, cnt) == cnt) 20561592Srgrimes byte_count += cnt; 20571592Srgrimes transflag = 0; 20581592Srgrimes (void)free(buf); 20591592Srgrimes if (cnt != 0) { 20601592Srgrimes if (cnt < 0) 20611592Srgrimes goto file_err; 20621592Srgrimes goto data_err; 20631592Srgrimes } 20641592Srgrimes reply(226, "Transfer complete."); 206589935Syar return (0); 20661592Srgrimes default: 20671592Srgrimes transflag = 0; 20681592Srgrimes reply(550, "Unimplemented TYPE %d in send_data", type); 206989935Syar return (-1); 20701592Srgrimes } 20711592Srgrimes 20721592Srgrimesdata_err: 20731592Srgrimes transflag = 0; 20741592Srgrimes perror_reply(426, "Data connection"); 207589935Syar return (-1); 20761592Srgrimes 20771592Srgrimesfile_err: 20781592Srgrimes transflag = 0; 20791592Srgrimes perror_reply(551, "Error on input file"); 208089935Syar return (-1); 208189935Syar 208289935Syargot_oob: 208389935Syar myoob(); 208489935Syar recvurg = 0; 208589935Syar transflag = 0; 208689935Syar return (-1); 20871592Srgrimes} 20881592Srgrimes 20891592Srgrimes/* 20901592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 20911592Srgrimes * the data subject to Mode, Structure, and Type. 20921592Srgrimes * 20931592Srgrimes * N.B.: Form isn't handled. 20941592Srgrimes */ 20951592Srgrimesstatic int 209690148Simpreceive_data(FILE *instr, FILE *outstr) 20971592Srgrimes{ 20981592Srgrimes int c; 209917433Spst int cnt, bare_lfs; 21001592Srgrimes char buf[BUFSIZ]; 21011592Srgrimes 21021592Srgrimes transflag++; 210317433Spst bare_lfs = 0; 210417433Spst 21051592Srgrimes switch (type) { 21061592Srgrimes 21071592Srgrimes case TYPE_I: 21081592Srgrimes case TYPE_L: 21091592Srgrimes while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 211089935Syar if (recvurg) 211189935Syar goto got_oob; 21121592Srgrimes if (write(fileno(outstr), buf, cnt) != cnt) 21131592Srgrimes goto file_err; 21141592Srgrimes byte_count += cnt; 21151592Srgrimes } 211689935Syar if (recvurg) 211789935Syar goto got_oob; 21181592Srgrimes if (cnt < 0) 21191592Srgrimes goto data_err; 21201592Srgrimes transflag = 0; 21211592Srgrimes return (0); 21221592Srgrimes 21231592Srgrimes case TYPE_E: 21241592Srgrimes reply(553, "TYPE E not implemented."); 21251592Srgrimes transflag = 0; 21261592Srgrimes return (-1); 21271592Srgrimes 21281592Srgrimes case TYPE_A: 21291592Srgrimes while ((c = getc(instr)) != EOF) { 213089935Syar if (recvurg) 213189935Syar goto got_oob; 21321592Srgrimes byte_count++; 21331592Srgrimes if (c == '\n') 21341592Srgrimes bare_lfs++; 21351592Srgrimes while (c == '\r') { 21361592Srgrimes if (ferror(outstr)) 21371592Srgrimes goto data_err; 21381592Srgrimes if ((c = getc(instr)) != '\n') { 21391592Srgrimes (void) putc ('\r', outstr); 21401592Srgrimes if (c == '\0' || c == EOF) 21411592Srgrimes goto contin2; 21421592Srgrimes } 21431592Srgrimes } 21441592Srgrimes (void) putc(c, outstr); 21451592Srgrimes contin2: ; 21461592Srgrimes } 214789935Syar if (recvurg) 214889935Syar goto got_oob; 21491592Srgrimes fflush(outstr); 21501592Srgrimes if (ferror(instr)) 21511592Srgrimes goto data_err; 21521592Srgrimes if (ferror(outstr)) 21531592Srgrimes goto file_err; 21541592Srgrimes transflag = 0; 21551592Srgrimes if (bare_lfs) { 21561592Srgrimes lreply(226, 21571592Srgrimes "WARNING! %d bare linefeeds received in ASCII mode", 21581592Srgrimes bare_lfs); 21591592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 21601592Srgrimes } 21611592Srgrimes return (0); 21621592Srgrimes default: 21631592Srgrimes reply(550, "Unimplemented TYPE %d in receive_data", type); 21641592Srgrimes transflag = 0; 21651592Srgrimes return (-1); 21661592Srgrimes } 21671592Srgrimes 21681592Srgrimesdata_err: 21691592Srgrimes transflag = 0; 21701592Srgrimes perror_reply(426, "Data Connection"); 21711592Srgrimes return (-1); 21721592Srgrimes 21731592Srgrimesfile_err: 21741592Srgrimes transflag = 0; 21751592Srgrimes perror_reply(452, "Error writing file"); 21761592Srgrimes return (-1); 217789935Syar 217889935Syargot_oob: 217989935Syar myoob(); 218089935Syar recvurg = 0; 218189935Syar transflag = 0; 218289935Syar return (-1); 21831592Srgrimes} 21841592Srgrimes 21851592Srgrimesvoid 218690148Simpstatfilecmd(char *filename) 21871592Srgrimes{ 21881592Srgrimes FILE *fin; 2189109382Syar int atstart; 21901592Srgrimes int c; 21911592Srgrimes char line[LINE_MAX]; 21921592Srgrimes 219325165Sdavidn (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 21941592Srgrimes fin = ftpd_popen(line, "r"); 21951592Srgrimes lreply(211, "status of %s:", filename); 2196109382Syar atstart = 1; 21971592Srgrimes while ((c = getc(fin)) != EOF) { 21981592Srgrimes if (c == '\n') { 21991592Srgrimes if (ferror(stdout)){ 22001592Srgrimes perror_reply(421, "control connection"); 22011592Srgrimes (void) ftpd_pclose(fin); 22021592Srgrimes dologout(1); 22031592Srgrimes /* NOTREACHED */ 22041592Srgrimes } 22051592Srgrimes if (ferror(fin)) { 22061592Srgrimes perror_reply(551, filename); 22071592Srgrimes (void) ftpd_pclose(fin); 22081592Srgrimes return; 22091592Srgrimes } 22101592Srgrimes (void) putc('\r', stdout); 22111592Srgrimes } 2212109382Syar /* 2213109382Syar * RFC 959 says neutral text should be prepended before 2214109382Syar * a leading 3-digit number followed by whitespace, but 2215109382Syar * many ftp clients can be confused by any leading digits, 2216109382Syar * as a matter of fact. 2217109382Syar */ 2218109382Syar if (atstart && isdigit(c)) 2219109382Syar (void) putc(' ', stdout); 22201592Srgrimes (void) putc(c, stdout); 2221109382Syar atstart = (c == '\n'); 22221592Srgrimes } 22231592Srgrimes (void) ftpd_pclose(fin); 22241592Srgrimes reply(211, "End of Status"); 22251592Srgrimes} 22261592Srgrimes 22271592Srgrimesvoid 222890148Simpstatcmd(void) 22291592Srgrimes{ 223056668Sshin union sockunion *su; 22311592Srgrimes u_char *a, *p; 223299255Sume char hname[NI_MAXHOST]; 223356668Sshin int ispassive; 22341592Srgrimes 2235110037Syar if (hostinfo) { 2236110037Syar lreply(211, "%s FTP server status:", hostname); 2237110037Syar printf(" %s\r\n", version); 2238110037Syar } else 2239110037Syar lreply(211, "FTP server status:"); 22401592Srgrimes printf(" Connected to %s", remotehost); 224156668Sshin if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 224299255Sume hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 224356668Sshin if (strcmp(hname, remotehost) != 0) 224456668Sshin printf(" (%s)", hname); 224556668Sshin } 22461592Srgrimes printf("\r\n"); 22471592Srgrimes if (logged_in) { 22481592Srgrimes if (guest) 22491592Srgrimes printf(" Logged in anonymously\r\n"); 22501592Srgrimes else 22511592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 22521592Srgrimes } else if (askpasswd) 22531592Srgrimes printf(" Waiting for password\r\n"); 22541592Srgrimes else 22551592Srgrimes printf(" Waiting for user name\r\n"); 22561592Srgrimes printf(" TYPE: %s", typenames[type]); 22571592Srgrimes if (type == TYPE_A || type == TYPE_E) 22581592Srgrimes printf(", FORM: %s", formnames[form]); 22591592Srgrimes if (type == TYPE_L) 2260103949Smike#if CHAR_BIT == 8 2261103949Smike printf(" %d", CHAR_BIT); 22621592Srgrimes#else 22631592Srgrimes printf(" %d", bytesize); /* need definition! */ 22641592Srgrimes#endif 22651592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 22661592Srgrimes strunames[stru], modenames[mode]); 22671592Srgrimes if (data != -1) 22681592Srgrimes printf(" Data connection open\r\n"); 22691592Srgrimes else if (pdata != -1) { 227056668Sshin ispassive = 1; 227156668Sshin su = &pasv_addr; 22721592Srgrimes goto printaddr; 22731592Srgrimes } else if (usedefault == 0) { 227456668Sshin ispassive = 0; 227556668Sshin su = &data_dest; 22761592Srgrimesprintaddr: 22771592Srgrimes#define UC(b) (((int) b) & 0xff) 227856668Sshin if (epsvall) { 227956668Sshin printf(" EPSV only mode (EPSV ALL)\r\n"); 228056668Sshin goto epsvonly; 228156668Sshin } 228256668Sshin 228356668Sshin /* PORT/PASV */ 228456668Sshin if (su->su_family == AF_INET) { 228556668Sshin a = (u_char *) &su->su_sin.sin_addr; 228656668Sshin p = (u_char *) &su->su_sin.sin_port; 228756668Sshin printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 228856668Sshin ispassive ? "PASV" : "PORT", 228956668Sshin UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 229056668Sshin UC(p[0]), UC(p[1])); 229156668Sshin } 229256668Sshin 229356668Sshin /* LPRT/LPSV */ 229456668Sshin { 229556668Sshin int alen, af, i; 229656668Sshin 229756668Sshin switch (su->su_family) { 229856668Sshin case AF_INET: 229956668Sshin a = (u_char *) &su->su_sin.sin_addr; 230056668Sshin p = (u_char *) &su->su_sin.sin_port; 230156668Sshin alen = sizeof(su->su_sin.sin_addr); 230256668Sshin af = 4; 230356668Sshin break; 230456668Sshin case AF_INET6: 230556668Sshin a = (u_char *) &su->su_sin6.sin6_addr; 230656668Sshin p = (u_char *) &su->su_sin6.sin6_port; 230756668Sshin alen = sizeof(su->su_sin6.sin6_addr); 230856668Sshin af = 6; 230956668Sshin break; 231056668Sshin default: 231156668Sshin af = 0; 231256668Sshin break; 231356668Sshin } 231456668Sshin if (af) { 231556668Sshin printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 231656668Sshin af, alen); 231756668Sshin for (i = 0; i < alen; i++) 231856668Sshin printf("%d,", UC(a[i])); 231956668Sshin printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 232056668Sshin } 232156668Sshin } 232256668Sshin 232356668Sshinepsvonly:; 232456668Sshin /* EPRT/EPSV */ 232556668Sshin { 232656668Sshin int af; 232756668Sshin 232856668Sshin switch (su->su_family) { 232956668Sshin case AF_INET: 233056668Sshin af = 1; 233156668Sshin break; 233256668Sshin case AF_INET6: 233356668Sshin af = 2; 233456668Sshin break; 233556668Sshin default: 233656668Sshin af = 0; 233756668Sshin break; 233856668Sshin } 233956668Sshin if (af) { 234099255Sume union sockunion tmp; 234199255Sume 234299255Sume tmp = *su; 234399255Sume if (tmp.su_family == AF_INET6) 234499255Sume tmp.su_sin6.sin6_scope_id = 0; 234599255Sume if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 234656668Sshin hname, sizeof(hname) - 1, NULL, 0, 234756668Sshin NI_NUMERICHOST)) { 234856668Sshin printf(" %s |%d|%s|%d|\r\n", 234956668Sshin ispassive ? "EPSV" : "EPRT", 235099255Sume af, hname, htons(tmp.su_port)); 235156668Sshin } 235256668Sshin } 235356668Sshin } 23541592Srgrimes#undef UC 23551592Srgrimes } else 23561592Srgrimes printf(" No data connection\r\n"); 23571592Srgrimes reply(211, "End of status"); 23581592Srgrimes} 23591592Srgrimes 23601592Srgrimesvoid 236190148Simpfatalerror(char *s) 23621592Srgrimes{ 23631592Srgrimes 23641592Srgrimes reply(451, "Error in server: %s\n", s); 23651592Srgrimes reply(221, "Closing connection due to server error."); 23661592Srgrimes dologout(0); 23671592Srgrimes /* NOTREACHED */ 23681592Srgrimes} 23691592Srgrimes 23701592Srgrimesvoid 23711592Srgrimesreply(int n, const char *fmt, ...) 23721592Srgrimes{ 23731592Srgrimes va_list ap; 237490148Simp 23751592Srgrimes va_start(ap, fmt); 23761592Srgrimes (void)printf("%d ", n); 23771592Srgrimes (void)vprintf(fmt, ap); 23781592Srgrimes (void)printf("\r\n"); 23791592Srgrimes (void)fflush(stdout); 238076096Smarkm if (ftpdebug) { 23811592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 23821592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 23831592Srgrimes } 23841592Srgrimes} 23851592Srgrimes 23861592Srgrimesvoid 23871592Srgrimeslreply(int n, const char *fmt, ...) 23881592Srgrimes{ 23891592Srgrimes va_list ap; 239090148Simp 23911592Srgrimes va_start(ap, fmt); 23921592Srgrimes (void)printf("%d- ", n); 23931592Srgrimes (void)vprintf(fmt, ap); 23941592Srgrimes (void)printf("\r\n"); 23951592Srgrimes (void)fflush(stdout); 239676096Smarkm if (ftpdebug) { 23971592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 23981592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 23991592Srgrimes } 24001592Srgrimes} 24011592Srgrimes 24021592Srgrimesstatic void 240390148Simpack(char *s) 24041592Srgrimes{ 24051592Srgrimes 24061592Srgrimes reply(250, "%s command successful.", s); 24071592Srgrimes} 24081592Srgrimes 24091592Srgrimesvoid 241090148Simpnack(char *s) 24111592Srgrimes{ 24121592Srgrimes 24131592Srgrimes reply(502, "%s command not implemented.", s); 24141592Srgrimes} 24151592Srgrimes 24161592Srgrimes/* ARGSUSED */ 24171592Srgrimesvoid 241890148Simpyyerror(char *s) 24191592Srgrimes{ 24201592Srgrimes char *cp; 24211592Srgrimes 242217478Smarkm if ((cp = strchr(cbuf,'\n'))) 24231592Srgrimes *cp = '\0'; 24241592Srgrimes reply(500, "'%s': command not understood.", cbuf); 24251592Srgrimes} 24261592Srgrimes 24271592Srgrimesvoid 242890148Simpdelete(char *name) 24291592Srgrimes{ 24301592Srgrimes struct stat st; 24311592Srgrimes 24321592Srgrimes LOGCMD("delete", name); 2433100439Syar if (lstat(name, &st) < 0) { 24341592Srgrimes perror_reply(550, name); 24351592Srgrimes return; 24361592Srgrimes } 24371592Srgrimes if ((st.st_mode&S_IFMT) == S_IFDIR) { 24381592Srgrimes if (rmdir(name) < 0) { 24391592Srgrimes perror_reply(550, name); 24401592Srgrimes return; 24411592Srgrimes } 24421592Srgrimes goto done; 24431592Srgrimes } 24441592Srgrimes if (unlink(name) < 0) { 24451592Srgrimes perror_reply(550, name); 24461592Srgrimes return; 24471592Srgrimes } 24481592Srgrimesdone: 24491592Srgrimes ack("DELE"); 24501592Srgrimes} 24511592Srgrimes 24521592Srgrimesvoid 245390148Simpcwd(char *path) 24541592Srgrimes{ 24551592Srgrimes 24561592Srgrimes if (chdir(path) < 0) 24571592Srgrimes perror_reply(550, path); 24581592Srgrimes else 24591592Srgrimes ack("CWD"); 24601592Srgrimes} 24611592Srgrimes 24621592Srgrimesvoid 246390148Simpmakedir(char *name) 24641592Srgrimes{ 2465100878Syar char *s; 24661592Srgrimes 24671592Srgrimes LOGCMD("mkdir", name); 246899195Smdodd if (guest && noguestmkd) 246999195Smdodd reply(550, "%s: permission denied", name); 247099195Smdodd else if (mkdir(name, 0777) < 0) 24711592Srgrimes perror_reply(550, name); 2472100878Syar else { 2473100878Syar if ((s = doublequote(name)) == NULL) 2474100878Syar fatalerror("Ran out of memory."); 2475100878Syar reply(257, "\"%s\" directory created.", s); 2476100878Syar free(s); 2477100878Syar } 24781592Srgrimes} 24791592Srgrimes 24801592Srgrimesvoid 248190148Simpremovedir(char *name) 24821592Srgrimes{ 24831592Srgrimes 24841592Srgrimes LOGCMD("rmdir", name); 24851592Srgrimes if (rmdir(name) < 0) 24861592Srgrimes perror_reply(550, name); 24871592Srgrimes else 24881592Srgrimes ack("RMD"); 24891592Srgrimes} 24901592Srgrimes 24911592Srgrimesvoid 249290148Simppwd(void) 24931592Srgrimes{ 2494100486Syar char *s, path[MAXPATHLEN + 1]; 24951592Srgrimes 24961592Srgrimes if (getwd(path) == (char *)NULL) 24971592Srgrimes reply(550, "%s.", path); 2498100486Syar else { 2499100486Syar if ((s = doublequote(path)) == NULL) 2500100486Syar fatalerror("Ran out of memory."); 2501100486Syar reply(257, "\"%s\" is current directory.", s); 2502100486Syar free(s); 2503100486Syar } 25041592Srgrimes} 25051592Srgrimes 25061592Srgrimeschar * 250790148Simprenamefrom(char *name) 25081592Srgrimes{ 25091592Srgrimes struct stat st; 25101592Srgrimes 2511100439Syar if (lstat(name, &st) < 0) { 25121592Srgrimes perror_reply(550, name); 25131592Srgrimes return ((char *)0); 25141592Srgrimes } 25151592Srgrimes reply(350, "File exists, ready for destination name"); 25161592Srgrimes return (name); 25171592Srgrimes} 25181592Srgrimes 25191592Srgrimesvoid 252090148Simprenamecmd(char *from, char *to) 25211592Srgrimes{ 252217433Spst struct stat st; 25231592Srgrimes 25241592Srgrimes LOGCMD2("rename", from, to); 252517433Spst 252617433Spst if (guest && (stat(to, &st) == 0)) { 252717433Spst reply(550, "%s: permission denied", to); 252817433Spst return; 252917433Spst } 253017433Spst 25311592Srgrimes if (rename(from, to) < 0) 25321592Srgrimes perror_reply(550, "rename"); 25331592Srgrimes else 25341592Srgrimes ack("RNTO"); 25351592Srgrimes} 25361592Srgrimes 25371592Srgrimesstatic void 253890148Simpdolog(struct sockaddr *who) 25391592Srgrimes{ 254056668Sshin int error; 25411592Srgrimes 254256668Sshin realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 254356668Sshin 25441592Srgrimes#ifdef SETPROCTITLE 254525283Sdavidn#ifdef VIRTUAL_HOSTING 254625283Sdavidn if (thishost != firsthost) 254725283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 254825283Sdavidn remotehost, hostname); 254925283Sdavidn else 255025283Sdavidn#endif 255125283Sdavidn snprintf(proctitle, sizeof(proctitle), "%s: connected", 255225283Sdavidn remotehost); 255313139Speter setproctitle("%s", proctitle); 25541592Srgrimes#endif /* SETPROCTITLE */ 25551592Srgrimes 255625283Sdavidn if (logging) { 255725283Sdavidn#ifdef VIRTUAL_HOSTING 255825283Sdavidn if (thishost != firsthost) 255925283Sdavidn syslog(LOG_INFO, "connection from %s (to %s)", 256025283Sdavidn remotehost, hostname); 256125283Sdavidn else 256225283Sdavidn#endif 256356668Sshin { 256456668Sshin char who_name[MAXHOSTNAMELEN]; 256556668Sshin 256656668Sshin error = getnameinfo(who, who->sa_len, 256756668Sshin who_name, sizeof(who_name) - 1, 256899255Sume NULL, 0, NI_NUMERICHOST); 256933782Seivind syslog(LOG_INFO, "connection from %s (%s)", remotehost, 257056668Sshin error == 0 ? who_name : ""); 257156668Sshin } 257225283Sdavidn } 25731592Srgrimes} 25741592Srgrimes 25751592Srgrimes/* 25761592Srgrimes * Record logout in wtmp file 25771592Srgrimes * and exit with supplied status. 25781592Srgrimes */ 25791592Srgrimesvoid 258090148Simpdologout(int status) 25811592Srgrimes{ 258222057Sdg /* 258322057Sdg * Prevent reception of SIGURG from resulting in a resumption 258422057Sdg * back to the main program loop. 258522058Sdg */ 258622057Sdg transflag = 0; 25871592Srgrimes 2588102311Syar if (logged_in && dowtmp) { 25891592Srgrimes (void) seteuid((uid_t)0); 259089920Sume ftpd_logwtmp(ttyline, "", NULL); 25911592Srgrimes } 25921592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 25931592Srgrimes _exit(status); 25941592Srgrimes} 25951592Srgrimes 25961592Srgrimesstatic void 259790148Simpsigurg(int signo) 25981592Srgrimes{ 259989935Syar 260089935Syar recvurg = 1; 260189935Syar} 260289935Syar 260389935Syarstatic void 260490148Simpmyoob(void) 260589935Syar{ 26061592Srgrimes char *cp; 26071592Srgrimes 26081592Srgrimes /* only process if transfer occurring */ 26091592Srgrimes if (!transflag) 26101592Srgrimes return; 26111592Srgrimes cp = tmpline; 26121592Srgrimes if (getline(cp, 7, stdin) == NULL) { 26131592Srgrimes reply(221, "You could at least say goodbye."); 26141592Srgrimes dologout(0); 26151592Srgrimes } 26161592Srgrimes upper(cp); 26171592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 26181592Srgrimes tmpline[0] = '\0'; 26191592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 26201592Srgrimes reply(226, "Abort successful"); 26211592Srgrimes } 26221592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 262351192Smharo tmpline[0] = '\0'; 26241592Srgrimes if (file_size != (off_t) -1) 26251592Srgrimes reply(213, "Status: %qd of %qd bytes transferred", 26261592Srgrimes byte_count, file_size); 26271592Srgrimes else 26281592Srgrimes reply(213, "Status: %qd bytes transferred", byte_count); 26291592Srgrimes } 26301592Srgrimes} 26311592Srgrimes 26321592Srgrimes/* 26331592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 26341592Srgrimes * the PASV command in RFC959. However, it has been blessed as 26351592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 26361592Srgrimes * with Rick Adams on 25 Jan 89. 26371592Srgrimes */ 26381592Srgrimesvoid 263990148Simppassive(void) 26401592Srgrimes{ 2641100615Syar int len, on; 26421592Srgrimes char *p, *a; 26431592Srgrimes 264417433Spst if (pdata >= 0) /* close old port if one set */ 264517433Spst close(pdata); 264617433Spst 264756668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 26481592Srgrimes if (pdata < 0) { 26491592Srgrimes perror_reply(425, "Can't open passive connection"); 26501592Srgrimes return; 26511592Srgrimes } 2652100615Syar on = 1; 2653100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2654100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 26559933Spst 265617433Spst (void) seteuid((uid_t)0); 265717433Spst 265819903Spst#ifdef IP_PORTRANGE 265956668Sshin if (ctrl_addr.su_family == AF_INET) { 2660100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2661100615Syar : IP_PORTRANGE_DEFAULT; 266219903Spst 266319903Spst if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2664100612Syar &on, sizeof(on)) < 0) 266519903Spst goto pasv_error; 26661592Srgrimes } 266719903Spst#endif 266860929Snsayer#ifdef IPV6_PORTRANGE 266960929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2670100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2671100615Syar : IPV6_PORTRANGE_DEFAULT; 26729933Spst 267360929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2674100612Syar &on, sizeof(on)) < 0) 267560929Snsayer goto pasv_error; 267660929Snsayer } 267760929Snsayer#endif 267860929Snsayer 267916033Speter pasv_addr = ctrl_addr; 268056668Sshin pasv_addr.su_port = 0; 268156668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 268216033Speter goto pasv_error; 268317433Spst 268416033Speter (void) seteuid((uid_t)pw->pw_uid); 268516033Speter 26861592Srgrimes len = sizeof(pasv_addr); 26871592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 26881592Srgrimes goto pasv_error; 26891592Srgrimes if (listen(pdata, 1) < 0) 26901592Srgrimes goto pasv_error; 269156668Sshin if (pasv_addr.su_family == AF_INET) 269256668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 269356668Sshin else if (pasv_addr.su_family == AF_INET6 && 269456668Sshin IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 269556668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 269656668Sshin else 269756668Sshin goto pasv_error; 269856668Sshin 269956668Sshin p = (char *) &pasv_addr.su_port; 27001592Srgrimes 27011592Srgrimes#define UC(b) (((int) b) & 0xff) 27021592Srgrimes 27031592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 27041592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 27051592Srgrimes return; 27061592Srgrimes 27071592Srgrimespasv_error: 270817433Spst (void) seteuid((uid_t)pw->pw_uid); 27091592Srgrimes (void) close(pdata); 27101592Srgrimes pdata = -1; 27111592Srgrimes perror_reply(425, "Can't open passive connection"); 27121592Srgrimes return; 27131592Srgrimes} 27141592Srgrimes 27151592Srgrimes/* 271656668Sshin * Long Passive defined in RFC 1639. 271756668Sshin * 228 Entering Long Passive Mode 271856668Sshin * (af, hal, h1, h2, h3,..., pal, p1, p2...) 271956668Sshin */ 272056668Sshin 272156668Sshinvoid 272290148Simplong_passive(char *cmd, int pf) 272356668Sshin{ 2724100615Syar int len, on; 272556668Sshin char *p, *a; 272656668Sshin 272756668Sshin if (pdata >= 0) /* close old port if one set */ 272856668Sshin close(pdata); 272956668Sshin 273056668Sshin if (pf != PF_UNSPEC) { 273156668Sshin if (ctrl_addr.su_family != pf) { 273256668Sshin switch (ctrl_addr.su_family) { 273356668Sshin case AF_INET: 273456668Sshin pf = 1; 273556668Sshin break; 273656668Sshin case AF_INET6: 273756668Sshin pf = 2; 273856668Sshin break; 273956668Sshin default: 274056668Sshin pf = 0; 274156668Sshin break; 274256668Sshin } 274356668Sshin /* 274456668Sshin * XXX 274556668Sshin * only EPRT/EPSV ready clients will understand this 274656668Sshin */ 274756668Sshin if (strcmp(cmd, "EPSV") == 0 && pf) { 274856668Sshin reply(522, "Network protocol mismatch, " 274956668Sshin "use (%d)", pf); 275056668Sshin } else 275156668Sshin reply(501, "Network protocol mismatch"); /*XXX*/ 275256668Sshin 275356668Sshin return; 275456668Sshin } 275556668Sshin } 275656668Sshin 275756668Sshin pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 275856668Sshin if (pdata < 0) { 275956668Sshin perror_reply(425, "Can't open passive connection"); 276056668Sshin return; 276156668Sshin } 2762100615Syar on = 1; 2763100615Syar if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2764100615Syar syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 276556668Sshin 276656668Sshin (void) seteuid((uid_t)0); 276756668Sshin 276856668Sshin pasv_addr = ctrl_addr; 276956668Sshin pasv_addr.su_port = 0; 277056668Sshin len = pasv_addr.su_len; 277156668Sshin 277260929Snsayer#ifdef IP_PORTRANGE 277360929Snsayer if (ctrl_addr.su_family == AF_INET) { 2774100615Syar on = restricted_data_ports ? IP_PORTRANGE_HIGH 2775100615Syar : IP_PORTRANGE_DEFAULT; 277660929Snsayer 277760929Snsayer if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2778100612Syar &on, sizeof(on)) < 0) 277960929Snsayer goto pasv_error; 278060929Snsayer } 278160929Snsayer#endif 278260929Snsayer#ifdef IPV6_PORTRANGE 278360929Snsayer if (ctrl_addr.su_family == AF_INET6) { 2784100615Syar on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2785100615Syar : IPV6_PORTRANGE_DEFAULT; 278660929Snsayer 278760929Snsayer if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2788100612Syar &on, sizeof(on)) < 0) 278960929Snsayer goto pasv_error; 279060929Snsayer } 279160929Snsayer#endif 279260929Snsayer 279356668Sshin if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 279456668Sshin goto pasv_error; 279556668Sshin 279656668Sshin (void) seteuid((uid_t)pw->pw_uid); 279756668Sshin 279856668Sshin if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 279956668Sshin goto pasv_error; 280056668Sshin if (listen(pdata, 1) < 0) 280156668Sshin goto pasv_error; 280256668Sshin 280356668Sshin#define UC(b) (((int) b) & 0xff) 280456668Sshin 280556668Sshin if (strcmp(cmd, "LPSV") == 0) { 280656668Sshin p = (char *)&pasv_addr.su_port; 280756668Sshin switch (pasv_addr.su_family) { 280856668Sshin case AF_INET: 280956668Sshin a = (char *) &pasv_addr.su_sin.sin_addr; 281056668Sshin v4_reply: 281156668Sshin reply(228, 281256668Sshin"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 281356668Sshin 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 281456668Sshin 2, UC(p[0]), UC(p[1])); 281556668Sshin return; 281656668Sshin case AF_INET6: 281756668Sshin if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 281856668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 281956668Sshin goto v4_reply; 282056668Sshin } 282156668Sshin a = (char *) &pasv_addr.su_sin6.sin6_addr; 282256668Sshin reply(228, 282356668Sshin"Entering Long Passive Mode " 282456668Sshin"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 282556668Sshin 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 282656668Sshin UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 282756668Sshin UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 282856668Sshin UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 282956668Sshin 2, UC(p[0]), UC(p[1])); 283056668Sshin return; 283156668Sshin } 283256668Sshin } else if (strcmp(cmd, "EPSV") == 0) { 283356668Sshin switch (pasv_addr.su_family) { 283456668Sshin case AF_INET: 283556668Sshin case AF_INET6: 283656668Sshin reply(229, "Entering Extended Passive Mode (|||%d|)", 283756668Sshin ntohs(pasv_addr.su_port)); 283856668Sshin return; 283956668Sshin } 284056668Sshin } else { 284156668Sshin /* more proper error code? */ 284256668Sshin } 284356668Sshin 284456668Sshinpasv_error: 284556668Sshin (void) seteuid((uid_t)pw->pw_uid); 284656668Sshin (void) close(pdata); 284756668Sshin pdata = -1; 284856668Sshin perror_reply(425, "Can't open passive connection"); 284956668Sshin return; 285056668Sshin} 285156668Sshin 285256668Sshin/* 2853101537Syar * Generate unique name for file with basename "local" 2854101537Syar * and open the file in order to avoid possible races. 2855101537Syar * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 2856101537Syar * Return descriptor to the file, set "name" to its name. 2857101537Syar * 28581592Srgrimes * Generates failure reply on error. 28591592Srgrimes */ 2860101537Syarstatic int 2861101537Syarguniquefd(char *local, char **name) 28621592Srgrimes{ 28631592Srgrimes static char new[MAXPATHLEN]; 28641592Srgrimes struct stat st; 2865101537Syar char *cp; 28661592Srgrimes int count; 2867101537Syar int fd; 28681592Srgrimes 28691592Srgrimes cp = strrchr(local, '/'); 28701592Srgrimes if (cp) 28711592Srgrimes *cp = '\0'; 28721592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 28731592Srgrimes perror_reply(553, cp ? local : "."); 2874101537Syar return (-1); 28751592Srgrimes } 2876101537Syar if (cp) { 2877101537Syar /* 2878101537Syar * Let not overwrite dirname with counter suffix. 2879101537Syar * -4 is for /nn\0 2880101537Syar * In this extreme case dot won't be put in front of suffix. 2881101537Syar */ 2882101537Syar if (strlen(local) > sizeof(new) - 4) { 2883101537Syar reply(553, "Pathname too long"); 2884101537Syar return (-1); 2885101537Syar } 28861592Srgrimes *cp = '/'; 2887101537Syar } 288831973Simp /* -4 is for the .nn<null> we put on the end below */ 288931973Simp (void) snprintf(new, sizeof(new) - 4, "%s", local); 28901592Srgrimes cp = new + strlen(new); 2891101537Syar /* 2892101537Syar * Don't generate dotfile unless requested explicitly. 2893101537Syar * This covers the case when basename gets truncated off 2894101537Syar * by buffer size. 2895101537Syar */ 2896101537Syar if (cp > new && cp[-1] != '/') 2897101537Syar *cp++ = '.'; 2898101537Syar for (count = 0; count < 100; count++) { 2899101537Syar /* At count 0 try unmodified name */ 2900101537Syar if (count) 2901101537Syar (void)sprintf(cp, "%d", count); 2902101537Syar if ((fd = open(count ? new : local, 2903101537Syar O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 2904101537Syar *name = count ? new : local; 2905101537Syar return (fd); 2906101537Syar } 2907110046Syar if (errno != EEXIST) { 2908110307Syar perror_reply(553, count ? new : local); 2909110046Syar return (-1); 2910110046Syar } 29111592Srgrimes } 29121592Srgrimes reply(452, "Unique file name cannot be created."); 2913101537Syar return (-1); 29141592Srgrimes} 29151592Srgrimes 29161592Srgrimes/* 29171592Srgrimes * Format and send reply containing system error number. 29181592Srgrimes */ 29191592Srgrimesvoid 292090148Simpperror_reply(int code, char *string) 29211592Srgrimes{ 29221592Srgrimes 29231592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 29241592Srgrimes} 29251592Srgrimes 29261592Srgrimesstatic char *onefile[] = { 29271592Srgrimes "", 29281592Srgrimes 0 29291592Srgrimes}; 29301592Srgrimes 29311592Srgrimesvoid 293290148Simpsend_file_list(char *whichf) 29331592Srgrimes{ 29341592Srgrimes struct stat st; 29351592Srgrimes DIR *dirp = NULL; 29361592Srgrimes struct dirent *dir; 29371592Srgrimes FILE *dout = NULL; 29381592Srgrimes char **dirlist, *dirname; 29391592Srgrimes int simple = 0; 29401592Srgrimes int freeglob = 0; 29411592Srgrimes glob_t gl; 29421592Srgrimes 29431592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 2944100222Smikeh int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 29451592Srgrimes 29461592Srgrimes memset(&gl, 0, sizeof(gl)); 294774470Sjlemon gl.gl_matchc = MAXGLOBARGS; 294880525Smikeh flags |= GLOB_LIMIT; 29491592Srgrimes freeglob = 1; 29501592Srgrimes if (glob(whichf, flags, 0, &gl)) { 29511592Srgrimes reply(550, "not found"); 29521592Srgrimes goto out; 29531592Srgrimes } else if (gl.gl_pathc == 0) { 29541592Srgrimes errno = ENOENT; 29551592Srgrimes perror_reply(550, whichf); 29561592Srgrimes goto out; 29571592Srgrimes } 29581592Srgrimes dirlist = gl.gl_pathv; 29591592Srgrimes } else { 29601592Srgrimes onefile[0] = whichf; 29611592Srgrimes dirlist = onefile; 29621592Srgrimes simple = 1; 29631592Srgrimes } 29641592Srgrimes 296517478Smarkm while ((dirname = *dirlist++)) { 29661592Srgrimes if (stat(dirname, &st) < 0) { 29671592Srgrimes /* 29681592Srgrimes * If user typed "ls -l", etc, and the client 29691592Srgrimes * used NLST, do what the user meant. 29701592Srgrimes */ 29711592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 29721592Srgrimes transflag == 0) { 297325165Sdavidn retrieve(_PATH_LS " %s", dirname); 29741592Srgrimes goto out; 29751592Srgrimes } 29761592Srgrimes perror_reply(550, whichf); 29771592Srgrimes if (dout != NULL) { 29781592Srgrimes (void) fclose(dout); 29791592Srgrimes transflag = 0; 29801592Srgrimes data = -1; 29811592Srgrimes pdata = -1; 29821592Srgrimes } 29831592Srgrimes goto out; 29841592Srgrimes } 29851592Srgrimes 29861592Srgrimes if (S_ISREG(st.st_mode)) { 29871592Srgrimes if (dout == NULL) { 29881592Srgrimes dout = dataconn("file list", (off_t)-1, "w"); 29891592Srgrimes if (dout == NULL) 29901592Srgrimes goto out; 29911592Srgrimes transflag++; 29921592Srgrimes } 29931592Srgrimes fprintf(dout, "%s%s\n", dirname, 29941592Srgrimes type == TYPE_A ? "\r" : ""); 29951592Srgrimes byte_count += strlen(dirname) + 1; 29961592Srgrimes continue; 29971592Srgrimes } else if (!S_ISDIR(st.st_mode)) 29981592Srgrimes continue; 29991592Srgrimes 30001592Srgrimes if ((dirp = opendir(dirname)) == NULL) 30011592Srgrimes continue; 30021592Srgrimes 30031592Srgrimes while ((dir = readdir(dirp)) != NULL) { 30041592Srgrimes char nbuf[MAXPATHLEN]; 30051592Srgrimes 300689935Syar if (recvurg) { 300789935Syar myoob(); 300889935Syar recvurg = 0; 300989935Syar transflag = 0; 301089935Syar goto out; 301189935Syar } 301289935Syar 30131592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 30141592Srgrimes continue; 30151592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 30161592Srgrimes dir->d_namlen == 2) 30171592Srgrimes continue; 30181592Srgrimes 301999213Smaxim snprintf(nbuf, sizeof(nbuf), 302031973Simp "%s/%s", dirname, dir->d_name); 30211592Srgrimes 30221592Srgrimes /* 30231592Srgrimes * We have to do a stat to insure it's 30241592Srgrimes * not a directory or special file. 30251592Srgrimes */ 30261592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 30271592Srgrimes S_ISREG(st.st_mode))) { 30281592Srgrimes if (dout == NULL) { 30291592Srgrimes dout = dataconn("file list", (off_t)-1, 30301592Srgrimes "w"); 30311592Srgrimes if (dout == NULL) 30321592Srgrimes goto out; 30331592Srgrimes transflag++; 30341592Srgrimes } 30351592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 30361592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 30371592Srgrimes type == TYPE_A ? "\r" : ""); 30381592Srgrimes else 30391592Srgrimes fprintf(dout, "%s%s\n", nbuf, 30401592Srgrimes type == TYPE_A ? "\r" : ""); 30411592Srgrimes byte_count += strlen(nbuf) + 1; 30421592Srgrimes } 30431592Srgrimes } 30441592Srgrimes (void) closedir(dirp); 30451592Srgrimes } 30461592Srgrimes 30471592Srgrimes if (dout == NULL) 30481592Srgrimes reply(550, "No files found."); 30491592Srgrimes else if (ferror(dout) != 0) 30501592Srgrimes perror_reply(550, "Data connection"); 30511592Srgrimes else 30521592Srgrimes reply(226, "Transfer complete."); 30531592Srgrimes 30541592Srgrimes transflag = 0; 30551592Srgrimes if (dout != NULL) 30561592Srgrimes (void) fclose(dout); 30571592Srgrimes data = -1; 30581592Srgrimes pdata = -1; 30591592Srgrimesout: 30601592Srgrimes if (freeglob) { 30611592Srgrimes freeglob = 0; 30621592Srgrimes globfree(&gl); 30631592Srgrimes } 30641592Srgrimes} 30651592Srgrimes 306615196Sdgvoid 306790148Simpreapchild(int signo) 306815196Sdg{ 306915196Sdg while (wait3(NULL, WNOHANG, NULL) > 0); 307015196Sdg} 307115196Sdg 307213139Speter#ifdef OLD_SETPROCTITLE 30731592Srgrimes/* 30741592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 30751592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 30761592Srgrimes * have much of an environment or arglist to overwrite. 30771592Srgrimes */ 30781592Srgrimesvoid 30791592Srgrimessetproctitle(const char *fmt, ...) 30801592Srgrimes{ 30811592Srgrimes int i; 30821592Srgrimes va_list ap; 30831592Srgrimes char *p, *bp, ch; 30841592Srgrimes char buf[LINE_MAX]; 30851592Srgrimes 30861592Srgrimes va_start(ap, fmt); 30871592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 30881592Srgrimes 30891592Srgrimes /* make ps print our process name */ 30901592Srgrimes p = Argv[0]; 30911592Srgrimes *p++ = '-'; 30921592Srgrimes 30931592Srgrimes i = strlen(buf); 30941592Srgrimes if (i > LastArgv - p - 2) { 30951592Srgrimes i = LastArgv - p - 2; 30961592Srgrimes buf[i] = '\0'; 30971592Srgrimes } 30981592Srgrimes bp = buf; 30991592Srgrimes while (ch = *bp++) 31001592Srgrimes if (ch != '\n' && ch != '\r') 31011592Srgrimes *p++ = ch; 31021592Srgrimes while (p < LastArgv) 31031592Srgrimes *p++ = ' '; 31041592Srgrimes} 310513139Speter#endif /* OLD_SETPROCTITLE */ 31066740Sguido 310717433Spststatic void 310890148Simplogxfer(char *name, off_t size, time_t start) 31096740Sguido{ 31106740Sguido char buf[1024]; 31116740Sguido char path[MAXPATHLEN + 1]; 311236612Sjb time_t now; 31136740Sguido 31146740Sguido if (statfd >= 0 && getwd(path) != NULL) { 31156740Sguido time(&now); 311682792Sache snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%qd!%ld\n", 31176740Sguido ctime(&now)+4, ident, remotehost, 311882792Sache path, name, (long long)size, 311982792Sache (long)(now - start + (now == start))); 31206740Sguido write(statfd, buf, strlen(buf)); 31216740Sguido } 31226740Sguido} 3123100486Syar 3124100486Syarstatic char * 3125100486Syardoublequote(char *s) 3126100486Syar{ 3127100486Syar int n; 3128100486Syar char *p, *s2; 3129100486Syar 3130100486Syar for (p = s, n = 0; *p; p++) 3131100486Syar if (*p == '"') 3132100486Syar n++; 3133100486Syar 3134100486Syar if ((s2 = malloc(p - s + n + 1)) == NULL) 3135100486Syar return (NULL); 3136100486Syar 3137100486Syar for (p = s2; *s; s++, p++) { 3138100486Syar if ((*p = *s) == '"') 3139100486Syar *(++p) = '"'; 3140100486Syar } 3141100486Syar *p = '\0'; 3142100486Syar 3143100486Syar return (s2); 3144100486Syar} 3145120059Sume 3146120059Sume/* setup server socket for specified address family */ 3147120059Sume/* if af is PF_UNSPEC more than one socket may be returned */ 3148120059Sume/* the returned list is dynamically allocated, so caller needs to free it */ 3149120059Sumestatic int * 3150120059Sumesocksetup(int af, char *bindname, const char *bindport) 3151120059Sume{ 3152120059Sume struct addrinfo hints, *res, *r; 3153120059Sume int error, maxs, *s, *socks; 3154120059Sume const int on = 1; 3155120059Sume 3156120059Sume memset(&hints, 0, sizeof(hints)); 3157120059Sume hints.ai_flags = AI_PASSIVE; 3158120059Sume hints.ai_family = af; 3159120059Sume hints.ai_socktype = SOCK_STREAM; 3160120059Sume error = getaddrinfo(bindname, bindport, &hints, &res); 3161120059Sume if (error) { 3162120059Sume syslog(LOG_ERR, "%s", gai_strerror(error)); 3163120059Sume if (error == EAI_SYSTEM) 3164120059Sume syslog(LOG_ERR, "%s", strerror(errno)); 3165120059Sume return NULL; 3166120059Sume } 3167120059Sume 3168120059Sume /* Count max number of sockets we may open */ 3169120059Sume for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 3170120059Sume ; 3171120059Sume socks = malloc((maxs + 1) * sizeof(int)); 3172120059Sume if (!socks) { 3173120059Sume freeaddrinfo(res); 3174120059Sume syslog(LOG_ERR, "couldn't allocate memory for sockets"); 3175120059Sume return NULL; 3176120059Sume } 3177120059Sume 3178120059Sume *socks = 0; /* num of sockets counter at start of array */ 3179120059Sume s = socks + 1; 3180120059Sume for (r = res; r; r = r->ai_next) { 3181120059Sume *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 3182120059Sume if (*s < 0) { 3183120059Sume syslog(LOG_DEBUG, "control socket: %m"); 3184120059Sume continue; 3185120059Sume } 3186120059Sume if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 3187120059Sume &on, sizeof(on)) < 0) 3188120059Sume syslog(LOG_WARNING, 3189120059Sume "control setsockopt (SO_REUSEADDR): %m"); 3190120059Sume if (r->ai_family == AF_INET6) { 3191120059Sume if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 3192120059Sume &on, sizeof(on)) < 0) 3193120059Sume syslog(LOG_WARNING, 3194120059Sume "control setsockopt (IPV6_V6ONLY): %m"); 3195120059Sume } 3196120059Sume if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 3197120059Sume syslog(LOG_DEBUG, "control bind: %m"); 3198120059Sume close(*s); 3199120059Sume continue; 3200120059Sume } 3201120059Sume (*socks)++; 3202120059Sume s++; 3203120059Sume } 3204120059Sume 3205120059Sume if (res) 3206120059Sume freeaddrinfo(res); 3207120059Sume 3208120059Sume if (*socks == 0) { 3209120059Sume syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); 3210120059Sume free(socks); 3211120059Sume return NULL; 3212120059Sume } 3213120059Sume return(socks); 3214120059Sume} 3215