ftpd.c revision 141918
118334Speter/* 290075Sobrien * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 3110611Skan * The Regents of the University of California. All rights reserved. 418334Speter * 590075Sobrien * Redistribution and use in source and binary forms, with or without 618334Speter * modification, are permitted provided that the following conditions 790075Sobrien * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 990075Sobrien * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1118334Speter * notice, this list of conditions and the following disclaimer in the 1290075Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 3. All advertising materials mentioning features or use of this software 1490075Sobrien * must display the following acknowledgement: 1590075Sobrien * This product includes software developed by the University of 1618334Speter * California, Berkeley and its contributors. 1718334Speter * 4. Neither the name of the University nor the names of its contributors 1890075Sobrien * may be used to endorse or promote products derived from this software 1990075Sobrien * without specific prior written permission. 2090075Sobrien * 2118334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2290075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2390075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2490075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2518334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2618334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2718334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2818334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2918334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3018334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3118334Speter * SUCH DAMAGE. 3218334Speter */ 3318334Speter 3418334Speter#if 0 3518334Speter#ifndef lint 3618334Speterstatic char copyright[] = 3718334Speter"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 3850397Sobrien The Regents of the University of California. All rights reserved.\n"; 3918334Speter#endif /* not lint */ 4090075Sobrien#endif 4118334Speter 4218334Speter#ifndef lint 4318334Speter#if 0 4418334Speterstatic char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; 4550397Sobrien#endif 4650397Sobrien#endif /* not lint */ 4790075Sobrien 4818334Speter#include <sys/cdefs.h> 4918334Speter__FBSDID("$FreeBSD: head/libexec/ftpd/ftpd.c 141918 2005-02-14 17:42:58Z stefanf $"); 5050397Sobrien 5150397Sobrien/* 5290075Sobrien * FTP server. 5390075Sobrien */ 5418334Speter#include <sys/param.h> 5518334Speter#include <sys/ioctl.h> 5618334Speter#include <sys/mman.h> 5718334Speter#include <sys/socket.h> 5818334Speter#include <sys/stat.h> 5918334Speter#include <sys/time.h> 6018334Speter#include <sys/wait.h> 6118334Speter 62110611Skan#include <netinet/in.h> 6390075Sobrien#include <netinet/in_systm.h> 6490075Sobrien#include <netinet/ip.h> 6590075Sobrien#include <netinet/tcp.h> 6690075Sobrien 6790075Sobrien#define FTP_NAMES 6890075Sobrien#include <arpa/ftp.h> 6990075Sobrien#include <arpa/inet.h> 7090075Sobrien#include <arpa/telnet.h> 7190075Sobrien 7290075Sobrien#include <ctype.h> 7390075Sobrien#include <dirent.h> 7452284Sobrien#include <err.h> 7552284Sobrien#include <errno.h> 7652284Sobrien#include <fcntl.h> 7752284Sobrien#include <glob.h> 7852284Sobrien#include <limits.h> 7952284Sobrien#include <netdb.h> 8052284Sobrien#include <pwd.h> 8190075Sobrien#include <grp.h> 8218334Speter#include <opie.h> 8318334Speter#include <signal.h> 8452284Sobrien#include <stdint.h> 8518334Speter#include <stdio.h> 8690075Sobrien#include <stdlib.h> 8750397Sobrien#include <string.h> 8890075Sobrien#include <syslog.h> 8990075Sobrien#include <time.h> 9090075Sobrien#include <unistd.h> 9118334Speter#include <libutil.h> 9218334Speter#ifdef LOGIN_CAP 9390075Sobrien#include <login_cap.h> 9490075Sobrien#endif 9590075Sobrien 9690075Sobrien#ifdef USE_PAM 9790075Sobrien#include <security/pam_appl.h> 9890075Sobrien#endif 9990075Sobrien 10090075Sobrien#include "pathnames.h" 10150397Sobrien#include "extern.h" 10290075Sobrien 10390075Sobrien#include <stdarg.h> 10490075Sobrien 10590075Sobrienstatic char version[] = "Version 6.00LS"; 10690075Sobrien#undef main 10790075Sobrien 10890075Sobrienextern off_t restart_point; 10990075Sobrienextern char cbuf[]; 11018334Speter 11190075Sobrienunion sockunion ctrl_addr; 11290075Sobrienunion sockunion data_source; 11318334Speterunion sockunion data_dest; 11490075Sobrienunion sockunion his_addr; 11590075Sobrienunion sockunion pasv_addr; 11690075Sobrien 11790075Sobrienint daemon_mode; 11890075Sobrienint data; 11918334Speterint dataport; 12018334Speterint hostinfo = 1; /* print host-specific info in messages */ 12190075Sobrienint logged_in; 12290075Sobrienstruct passwd *pw; 123110611Skanchar *homedir; 124110611Skanint ftpdebug; 125110611Skanint timeout = 900; /* timeout after 15 minutes of inactivity */ 126110611Skanint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 127110611Skanint logging; 128110611Skanint restricted_data_ports = 1; 129110611Skanint paranoid = 1; /* be extra careful about security */ 130110611Skanint anon_only = 0; /* Only anonymous ftp allowed */ 131110611Skanint guest; 132110611Skanint dochroot; 133110611Skanchar *chrootdir; 134110611Skanint dowtmp = 1; 135110611Skanint stats; 136110611Skanint statfd = -1; 137110611Skanint type; 138110611Skanint form; 139110611Skanint stru; /* avoid C keyword */ 140110611Skanint mode; 141110611Skanint usedefault = 1; /* for data transfers */ 142110611Skanint pdata = -1; /* for passive mode */ 143110611Skanint readonly = 0; /* Server is in readonly mode. */ 14490075Sobrienint noepsv = 0; /* EPSV command is disabled. */ 14590075Sobrienint noretr = 0; /* RETR command is disabled. */ 14690075Sobrienint noguestretr = 0; /* RETR command is disabled for anon users. */ 14790075Sobrienint noguestmkd = 0; /* MKD command is disabled for anon users. */ 14890075Sobrienint noguestmod = 1; /* anon users may not modify existing files. */ 14918334Speter 15090075Sobrienoff_t file_size; 15118334Speteroff_t byte_count; 15290075Sobrien#if !defined(CMASK) || CMASK == 0 15318334Speter#undef CMASK 15490075Sobrien#define CMASK 027 15590075Sobrien#endif 15690075Sobrienint defumask = CMASK; /* default umask value */ 15790075Sobrienchar tmpline[7]; 15890075Sobrienchar *hostname; 15990075Sobrienint epsvall = 0; 16090075Sobrien 16190075Sobrien#ifdef VIRTUAL_HOSTING 162110611Skanchar *ftpuser; 16390075Sobrien 16418334Speterstatic struct ftphost { 16590075Sobrien struct ftphost *next; 16690075Sobrien struct addrinfo *hostinfo; 16718334Speter char *hostname; 16890075Sobrien char *anonuser; 16918334Speter char *statfile; 17018334Speter char *welcome; 17118334Speter char *loginmsg; 17290075Sobrien} *thishost, *firsthost; 17318334Speter 17490075Sobrien#endif 17590075Sobrienchar remotehost[NI_MAXHOST]; 17690075Sobrienchar *ident = NULL; 17790075Sobrien 17890075Sobrienstatic char ttyline[20]; 17990075Sobrienchar *tty = ttyline; /* for klogin */ 18018334Speter 18118334Speter#ifdef USE_PAM 18218334Speterstatic int auth_pam(struct passwd**, const char*); 18318334Speterpam_handle_t *pamh = NULL; 18418334Speter#endif 18590075Sobrien 18690075Sobrienstatic struct opie opiedata; 18790075Sobrienstatic char opieprompt[OPIE_CHALLENGE_MAX+1]; 18890075Sobrienstatic int pwok; 18990075Sobrien 19090075Sobrienchar *pid_file = NULL; 19190075Sobrien 19290075Sobrien/* 19390075Sobrien * Limit number of pathnames that glob can return. 19490075Sobrien * A limit of 0 indicates the number of pathnames is unlimited. 19590075Sobrien */ 19690075Sobrien#define MAXGLOBARGS 16384 19790075Sobrien# 19890075Sobrien 19990075Sobrien/* 20090075Sobrien * Timeout intervals for retrying connections 20190075Sobrien * to hosts that don't accept PORT cmds. This 20218334Speter * is a kludge, but given the problems with TCP... 20390075Sobrien */ 20490075Sobrien#define SWAITMAX 90 /* wait at most 90 seconds */ 20590075Sobrien#define SWAITINT 5 /* interval between retries */ 20652284Sobrien 20752284Sobrienint swaitmax = SWAITMAX; 20852284Sobrienint swaitint = SWAITINT; 20952284Sobrien 21052284Sobrien#ifdef SETPROCTITLE 21152284Sobrien#ifdef OLD_SETPROCTITLE 21252284Sobrienchar **Argv = NULL; /* pointer to argument vector */ 21352284Sobrienchar *LastArgv = NULL; /* end of argv */ 21452284Sobrien#endif /* OLD_SETPROCTITLE */ 21552284Sobrienchar proctitle[LINE_MAX]; /* initial part of title */ 21652284Sobrien#endif /* SETPROCTITLE */ 21752284Sobrien 21852284Sobrien#define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1) 21952284Sobrien#define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1) 22052284Sobrien#define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt)) 22152284Sobrien 22252284Sobrienstatic volatile sig_atomic_t recvurg; 22352284Sobrienstatic int transflag; /* NB: for debugging only */ 22452284Sobrien 22552284Sobrien#define STARTXFER flagxfer(1) 22652284Sobrien#define ENDXFER flagxfer(0) 22752284Sobrien 22852284Sobrien#define START_UNSAFE maskurg(1) 22952284Sobrien#define END_UNSAFE maskurg(0) 23052284Sobrien 23152284Sobrien/* It's OK to put an `else' clause after this macro. */ 23252284Sobrien#define CHECKOOB(action) \ 23352284Sobrien if (recvurg) { \ 23452284Sobrien recvurg = 0; \ 23552284Sobrien if (myoob()) { \ 23652284Sobrien ENDXFER; \ 23752284Sobrien action; \ 23852284Sobrien } \ 23952284Sobrien } 24052284Sobrien 24152284Sobrien#ifdef VIRTUAL_HOSTING 24252284Sobrienstatic void inithosts(void); 24352284Sobrienstatic void selecthost(union sockunion *); 24490075Sobrien#endif 24552284Sobrienstatic void ack(char *); 24652284Sobrienstatic void sigurg(int); 24790075Sobrienstatic void maskurg(int); 24852284Sobrienstatic void flagxfer(int); 24952284Sobrienstatic int myoob(void); 25052284Sobrienstatic int checkuser(char *, char *, int, char **); 25152284Sobrienstatic FILE *dataconn(char *, off_t, char *); 25252284Sobrienstatic void dolog(struct sockaddr *); 25390075Sobrienstatic void end_login(void); 25452284Sobrienstatic FILE *getdatasock(char *); 25590075Sobrienstatic int guniquefd(char *, char **); 25690075Sobrienstatic void lostconn(int); 25752284Sobrienstatic void sigquit(int); 25890075Sobrienstatic int receive_data(FILE *, FILE *); 25990075Sobrienstatic int send_data(FILE *, FILE *, size_t, off_t, int); 26090075Sobrienstatic struct passwd * 26190075Sobrien sgetpwnam(char *); 26290075Sobrienstatic char *sgetsave(char *); 26390075Sobrienstatic void reapchild(int); 26490075Sobrienstatic void appendf(char **, char *, ...) __printflike(2, 3); 26552284Sobrienstatic void logcmd(char *, char *, char *, off_t); 26690075Sobrienstatic void logxfer(char *, off_t, time_t); 26790075Sobrienstatic char *doublequote(char *); 26890075Sobrienstatic int *socksetup(int, char *, const char *); 26990075Sobrien 27052284Sobrienint 27190075Sobrienmain(int argc, char *argv[], char **envp) 27290075Sobrien{ 27390075Sobrien socklen_t addrlen; 27490075Sobrien int ch, on = 1, tos; 27590075Sobrien char *cp, line[LINE_MAX]; 27690075Sobrien FILE *fd; 27790075Sobrien char *bindname = NULL; 27890075Sobrien const char *bindport = "ftp"; 27990075Sobrien int family = AF_UNSPEC; 28090075Sobrien struct sigaction sa; 28190075Sobrien 28252284Sobrien tzset(); /* in case no timezone database in ~ftp */ 28390075Sobrien sigemptyset(&sa.sa_mask); 28490075Sobrien sa.sa_flags = SA_RESTART; 28590075Sobrien 28690075Sobrien#ifdef OLD_SETPROCTITLE 28790075Sobrien /* 28890075Sobrien * Save start and extent of argv for setproctitle. 28990075Sobrien */ 29090075Sobrien Argv = argv; 29190075Sobrien while (*envp) 29290075Sobrien envp++; 29390075Sobrien LastArgv = envp[-1] + strlen(envp[-1]); 29490075Sobrien#endif /* OLD_SETPROCTITLE */ 29552284Sobrien 29652284Sobrien /* 29752284Sobrien * Prevent diagnostic messages from appearing on stderr. 29852284Sobrien * We run as a daemon or from inetd; in both cases, there's 29952284Sobrien * more reason in logging to syslog. 30018334Speter */ 30118334Speter (void) freopen(_PATH_DEVNULL, "w", stderr); 30218334Speter opterr = 0; 30318334Speter 30418334Speter /* 30518334Speter * LOG_NDELAY sets up the logging connection immediately, 30618334Speter * necessary for anonymous ftp's that chroot and can't do it later. 30718334Speter */ 30818334Speter openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 30918334Speter 31018334Speter while ((ch = getopt(argc, argv, 31118334Speter "46a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) { 31218334Speter switch (ch) { 31318334Speter case '4': 31418334Speter family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; 31518334Speter break; 31652750Sobrien 31718334Speter case '6': 318110611Skan family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; 319110611Skan break; 32018334Speter 32118334Speter case 'a': 32218334Speter bindname = optarg; 32390075Sobrien break; 32418334Speter 32518334Speter case 'A': 32618334Speter anon_only = 1; 32718334Speter break; 32818334Speter 32918334Speter case 'd': 33018334Speter ftpdebug++; 33118334Speter break; 33252284Sobrien 33318334Speter case 'D': 33450397Sobrien daemon_mode++; 33550397Sobrien break; 33650397Sobrien 33750397Sobrien case 'E': 33818334Speter noepsv = 1; 33918334Speter break; 34018334Speter 34118334Speter case 'h': 34218334Speter hostinfo = 0; 34318334Speter break; 34418334Speter 34518334Speter case 'l': 34618334Speter logging++; /* > 1 == extra logging */ 34718334Speter break; 34818334Speter 34918334Speter case 'm': 35018334Speter noguestmod = 0; 35118334Speter break; 35218334Speter 35318334Speter case 'M': 35418334Speter noguestmkd = 1; 35518334Speter break; 35618334Speter 35718334Speter case 'o': 35818334Speter noretr = 1; 35918334Speter break; 36018334Speter 36118334Speter case 'O': 36218334Speter noguestretr = 1; 36318334Speter break; 36450397Sobrien 36550397Sobrien case 'p': 36650397Sobrien pid_file = optarg; 36750397Sobrien break; 36850397Sobrien 36950397Sobrien case 'P': 37050397Sobrien bindport = optarg; 37150397Sobrien break; 37250397Sobrien 37350397Sobrien case 'r': 37418334Speter readonly = 1; 37518334Speter break; 37618334Speter 37752284Sobrien case 'R': 37852284Sobrien paranoid = 0; 37952284Sobrien break; 38052284Sobrien 38118334Speter case 'S': 38218334Speter stats++; 38390075Sobrien break; 38418334Speter 38518334Speter case 't': 38650397Sobrien timeout = atoi(optarg); 38750397Sobrien if (maxtimeout < timeout) 38818334Speter maxtimeout = timeout; 38918334Speter break; 39018334Speter 39118334Speter case 'T': 39218334Speter maxtimeout = atoi(optarg); 39318334Speter if (timeout > maxtimeout) 39418334Speter timeout = maxtimeout; 39518334Speter break; 39618334Speter 39718334Speter case 'u': 39818334Speter { 39918334Speter long val = 0; 40018334Speter 40118334Speter val = strtol(optarg, &optarg, 8); 40218334Speter if (*optarg != '\0' || val < 0) 40318334Speter syslog(LOG_WARNING, "bad value for -u"); 40418334Speter else 40518334Speter defumask = val; 40650397Sobrien break; 40718334Speter } 40818334Speter case 'U': 40950397Sobrien restricted_data_ports = 0; 41018334Speter break; 41118334Speter 41218334Speter case 'v': 41318334Speter ftpdebug++; 41418334Speter break; 41518334Speter 41618334Speter case 'W': 41790075Sobrien dowtmp = 0; 41818334Speter break; 41918334Speter 42018334Speter default: 42118334Speter syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); 42218334Speter break; 42318334Speter } 42490075Sobrien } 42518334Speter 42618334Speter#ifdef VIRTUAL_HOSTING 42718334Speter inithosts(); 42852750Sobrien#endif 42952750Sobrien 43090075Sobrien if (daemon_mode) { 43152750Sobrien int *ctl_sock, fd, maxfd = -1, nfds, i; 43252750Sobrien fd_set defreadfds, readfds; 43352750Sobrien pid_t pid; 43452750Sobrien 43552750Sobrien /* 43652750Sobrien * Detach from parent. 43752750Sobrien */ 43852750Sobrien if (daemon(1, 1) < 0) { 43952750Sobrien syslog(LOG_ERR, "failed to become a daemon"); 44052750Sobrien exit(1); 44152750Sobrien } 44218334Speter sa.sa_handler = reapchild; 44390075Sobrien (void)sigaction(SIGCHLD, &sa, NULL); 44490075Sobrien 44590075Sobrien /* 44690075Sobrien * Open a socket, bind it to the FTP port, and start 44718334Speter * listening. 44890075Sobrien */ 44918334Speter ctl_sock = socksetup(family, bindname, bindport); 45090075Sobrien if (ctl_sock == NULL) 45190075Sobrien exit(1); 45290075Sobrien 45390075Sobrien FD_ZERO(&defreadfds); 45490075Sobrien for (i = 1; i <= *ctl_sock; i++) { 45590075Sobrien FD_SET(ctl_sock[i], &defreadfds); 45690075Sobrien if (listen(ctl_sock[i], 32) < 0) { 45790075Sobrien syslog(LOG_ERR, "control listen: %m"); 45890075Sobrien exit(1); 45990075Sobrien } 46090075Sobrien if (maxfd < ctl_sock[i]) 46190075Sobrien maxfd = ctl_sock[i]; 46290075Sobrien } 46390075Sobrien 46490075Sobrien /* 46590075Sobrien * Atomically write process ID 46618334Speter */ 46790075Sobrien if (pid_file) 46890075Sobrien { 46990075Sobrien int fd; 47090075Sobrien char buf[20]; 47152750Sobrien 47290075Sobrien fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC 47390075Sobrien | O_NONBLOCK | O_EXLOCK, 0644); 47452750Sobrien if (fd < 0) { 47552750Sobrien if (errno == EAGAIN) 47652750Sobrien syslog(LOG_ERR, 47752750Sobrien "%s: already locked", pid_file); 47852750Sobrien else 47990075Sobrien syslog(LOG_ERR, "%s: %m", pid_file); 48052750Sobrien exit(1); 48152750Sobrien } 48252750Sobrien snprintf(buf, sizeof(buf), 48318334Speter "%lu\n", (unsigned long) getpid()); 48490075Sobrien if (write(fd, buf, strlen(buf)) < 0) { 48590075Sobrien syslog(LOG_ERR, "%s: write: %m", pid_file); 48690075Sobrien exit(1); 48790075Sobrien } 48852750Sobrien /* Leave the pid file open and locked */ 48990075Sobrien } 49090075Sobrien /* 49190075Sobrien * Loop forever accepting connection requests and forking off 49290075Sobrien * children to handle them. 49390075Sobrien */ 49490075Sobrien while (1) { 49590075Sobrien FD_COPY(&defreadfds, &readfds); 49690075Sobrien nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); 49790075Sobrien if (nfds <= 0) { 49890075Sobrien if (nfds < 0 && errno != EINTR) 49990075Sobrien syslog(LOG_WARNING, "select: %m"); 50090075Sobrien continue; 50190075Sobrien } 50290075Sobrien 50390075Sobrien pid = -1; 50490075Sobrien for (i = 1; i <= *ctl_sock; i++) 50590075Sobrien if (FD_ISSET(ctl_sock[i], &readfds)) { 50652750Sobrien addrlen = sizeof(his_addr); 50752750Sobrien fd = accept(ctl_sock[i], 50818334Speter (struct sockaddr *)&his_addr, 50952750Sobrien &addrlen); 51052750Sobrien if (fd >= 0) { 51152750Sobrien if ((pid = fork()) == 0) { 51218334Speter /* child */ 51352750Sobrien (void) dup2(fd, 0); 51452750Sobrien (void) dup2(fd, 1); 51552750Sobrien close(ctl_sock[i]); 51652750Sobrien } else 51752750Sobrien close(fd); 51852750Sobrien } 51918334Speter } 52018334Speter if (pid == 0) 52118334Speter break; 52218334Speter } 52318334Speter } else { 52418334Speter addrlen = sizeof(his_addr); 52552750Sobrien if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 52652750Sobrien syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 52752750Sobrien exit(1); 52852750Sobrien } 52952750Sobrien } 53052750Sobrien 53152750Sobrien sa.sa_handler = SIG_DFL; 53252750Sobrien (void)sigaction(SIGCHLD, &sa, NULL); 53318334Speter 53418334Speter sa.sa_handler = sigurg; 53518334Speter sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ 53618334Speter (void)sigaction(SIGURG, &sa, NULL); 53790075Sobrien 53890075Sobrien sigfillset(&sa.sa_mask); /* block all signals in handler */ 53952750Sobrien sa.sa_flags = SA_RESTART; 54052750Sobrien sa.sa_handler = sigquit; 54152750Sobrien (void)sigaction(SIGHUP, &sa, NULL); 54252750Sobrien (void)sigaction(SIGINT, &sa, NULL); 54352750Sobrien (void)sigaction(SIGQUIT, &sa, NULL); 54418334Speter (void)sigaction(SIGTERM, &sa, NULL); 54518334Speter 54618334Speter sa.sa_handler = lostconn; 54790075Sobrien (void)sigaction(SIGPIPE, &sa, NULL); 54818334Speter 54990075Sobrien addrlen = sizeof(ctrl_addr); 55090075Sobrien if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 55190075Sobrien syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 55290075Sobrien exit(1); 55318334Speter } 55418334Speter dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ 55518334Speter#ifdef VIRTUAL_HOSTING 55690075Sobrien /* select our identity from virtual host table */ 55790075Sobrien selecthost(&ctrl_addr); 55890075Sobrien#endif 55990075Sobrien#ifdef IP_TOS 56090075Sobrien if (ctrl_addr.su_family == AF_INET) 56118334Speter { 56290075Sobrien tos = IPTOS_LOWDELAY; 56390075Sobrien if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 56490075Sobrien syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); 56590075Sobrien } 56618334Speter#endif 56790075Sobrien /* 56890075Sobrien * Disable Nagle on the control channel so that we don't have to wait 56990075Sobrien * for peer's ACK before issuing our next reply. 57018334Speter */ 57118334Speter if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 57290075Sobrien syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); 57390075Sobrien 57418334Speter data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); 57590075Sobrien 57618334Speter /* set this here so klogin can use it... */ 57718334Speter (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); 57818334Speter 57918334Speter /* Try to handle urgent data inline */ 58018334Speter#ifdef SO_OOBINLINE 58118334Speter if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) 58218334Speter syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); 58318334Speter#endif 58418334Speter 58518334Speter#ifdef F_SETOWN 58618334Speter if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 58718334Speter syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 58818334Speter#endif 58918334Speter dolog((struct sockaddr *)&his_addr); 59018334Speter /* 59118334Speter * Set up default state 59218334Speter */ 59318334Speter data = -1; 59418334Speter type = TYPE_A; 59518334Speter form = FORM_N; 59618334Speter stru = STRU_F; 59718334Speter mode = MODE_S; 59818334Speter tmpline[0] = '\0'; 59990075Sobrien 60090075Sobrien /* If logins are disabled, print out the message. */ 60118334Speter if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 60218334Speter while (fgets(line, sizeof(line), fd) != NULL) { 60390075Sobrien if ((cp = strchr(line, '\n')) != NULL) 60490075Sobrien *cp = '\0'; 60590075Sobrien lreply(530, "%s", line); 60618334Speter } 60790075Sobrien (void) fflush(stdout); 60818334Speter (void) fclose(fd); 60990075Sobrien reply(530, "System not available."); 61090075Sobrien exit(0); 61190075Sobrien } 61218334Speter#ifdef VIRTUAL_HOSTING 61318334Speter fd = fopen(thishost->welcome, "r"); 61418334Speter#else 61518334Speter fd = fopen(_PATH_FTPWELCOME, "r"); 61618334Speter#endif 61718334Speter if (fd != NULL) { 61818334Speter while (fgets(line, sizeof(line), fd) != NULL) { 61918334Speter if ((cp = strchr(line, '\n')) != NULL) 62018334Speter *cp = '\0'; 62118334Speter lreply(220, "%s", line); 62218334Speter } 62318334Speter (void) fflush(stdout); 62418334Speter (void) fclose(fd); 62518334Speter /* reply(220,) must follow */ 62618334Speter } 62718334Speter#ifndef VIRTUAL_HOSTING 62818334Speter if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 62918334Speter fatalerror("Ran out of memory."); 63018334Speter if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 63118334Speter hostname[0] = '\0'; 63218334Speter hostname[MAXHOSTNAMELEN - 1] = '\0'; 63318334Speter#endif 63418334Speter if (hostinfo) 63518334Speter reply(220, "%s FTP server (%s) ready.", hostname, version); 63618334Speter else 63718334Speter reply(220, "FTP server ready."); 63818334Speter for (;;) 63918334Speter (void) yyparse(); 64018334Speter /* NOTREACHED */ 64118334Speter} 64218334Speter 64318334Speterstatic void 64418334Speterlostconn(int signo) 64518334Speter{ 64618334Speter 64718334Speter if (ftpdebug) 64818334Speter syslog(LOG_DEBUG, "lost connection"); 64918334Speter dologout(1); 65018334Speter} 65118334Speter 65218334Speterstatic void 65318334Spetersigquit(int signo) 65418334Speter{ 65518334Speter 65618334Speter syslog(LOG_ERR, "got signal %d", signo); 65718334Speter dologout(1); 65890075Sobrien} 65990075Sobrien 66090075Sobrien#ifdef VIRTUAL_HOSTING 66190075Sobrien/* 66290075Sobrien * read in virtual host tables (if they exist) 66390075Sobrien */ 66490075Sobrien 66590075Sobrienstatic void 66690075Sobrieninithosts(void) 66790075Sobrien{ 66818334Speter int insert; 66990075Sobrien size_t len; 67018334Speter FILE *fp; 67190075Sobrien char *cp, *mp, *line; 67290075Sobrien char *hostname; 67390075Sobrien char *vhost, *anonuser, *statfile, *welcome, *loginmsg; 67418334Speter struct ftphost *hrp, *lhrp; 67590075Sobrien struct addrinfo hints, *res, *ai; 67690075Sobrien 67790075Sobrien /* 67818334Speter * Fill in the default host information 67990075Sobrien */ 68090075Sobrien if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) 68190075Sobrien fatalerror("Ran out of memory."); 68290075Sobrien if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) 68390075Sobrien hostname[0] = '\0'; 68490075Sobrien hostname[MAXHOSTNAMELEN - 1] = '\0'; 68518334Speter if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 68690075Sobrien fatalerror("Ran out of memory."); 68790075Sobrien hrp->hostname = hostname; 68890075Sobrien hrp->hostinfo = NULL; 68990075Sobrien 69018334Speter memset(&hints, 0, sizeof(hints)); 69190075Sobrien hints.ai_flags = AI_CANONNAME; 69218334Speter hints.ai_family = AF_UNSPEC; 69390075Sobrien if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) 69490075Sobrien hrp->hostinfo = res; 69518334Speter hrp->statfile = _PATH_FTPDSTATFILE; 69690075Sobrien hrp->welcome = _PATH_FTPWELCOME; 69790075Sobrien hrp->loginmsg = _PATH_FTPLOGINMESG; 69890075Sobrien hrp->anonuser = "ftp"; 69990075Sobrien hrp->next = NULL; 70090075Sobrien thishost = firsthost = lhrp = hrp; 70190075Sobrien if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { 70290075Sobrien int addrsize, gothost; 70390075Sobrien void *addr; 70490075Sobrien struct hostent *hp; 70590075Sobrien 70690075Sobrien while ((line = fgetln(fp, &len)) != NULL) { 70790075Sobrien int i, hp_error; 70890075Sobrien 70990075Sobrien /* skip comments */ 71090075Sobrien if (line[0] == '#') 71190075Sobrien continue; 71290075Sobrien if (line[len - 1] == '\n') { 71390075Sobrien line[len - 1] = '\0'; 71490075Sobrien mp = NULL; 71590075Sobrien } else { 71690075Sobrien if ((mp = malloc(len + 1)) == NULL) 71790075Sobrien fatalerror("Ran out of memory."); 71890075Sobrien memcpy(mp, line, len); 71990075Sobrien mp[len] = '\0'; 72090075Sobrien line = mp; 72118334Speter } 72218334Speter cp = strtok(line, " \t"); 72390075Sobrien /* skip empty lines */ 72418334Speter if (cp == NULL) 72590075Sobrien goto nextline; 72690075Sobrien vhost = cp; 72718334Speter 72890075Sobrien /* set defaults */ 72918334Speter anonuser = "ftp"; 73018334Speter statfile = _PATH_FTPDSTATFILE; 73118334Speter welcome = _PATH_FTPWELCOME; 73218334Speter loginmsg = _PATH_FTPLOGINMESG; 73318334Speter 73490075Sobrien /* 73590075Sobrien * Preparse the line so we can use its info 73690075Sobrien * for all the addresses associated with 73790075Sobrien * the virtual host name. 73890075Sobrien * Field 0, the virtual host name, is special: 73990075Sobrien * it's already parsed off and will be strdup'ed 74090075Sobrien * later, after we know its canonical form. 74118334Speter */ 74290075Sobrien for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) 74390075Sobrien if (*cp != '-' && (cp = strdup(cp))) 74490075Sobrien switch (i) { 74590075Sobrien case 1: /* anon user permissions */ 74690075Sobrien anonuser = cp; 74790075Sobrien break; 74890075Sobrien case 2: /* statistics file */ 74990075Sobrien statfile = cp; 75090075Sobrien break; 75152284Sobrien case 3: /* welcome message */ 75290075Sobrien welcome = cp; 75390075Sobrien break; 75490075Sobrien case 4: /* login message */ 75590075Sobrien loginmsg = cp; 75690075Sobrien break; 75790075Sobrien default: /* programming error */ 75890075Sobrien abort(); 75990075Sobrien /* NOTREACHED */ 76090075Sobrien } 76190075Sobrien 76290075Sobrien hints.ai_flags = 0; 76390075Sobrien hints.ai_family = AF_UNSPEC; 76490075Sobrien hints.ai_flags = AI_PASSIVE; 76590075Sobrien if (getaddrinfo(vhost, NULL, &hints, &res) != 0) 76690075Sobrien goto nextline; 76790075Sobrien for (ai = res; ai != NULL && ai->ai_addr != NULL; 76890075Sobrien ai = ai->ai_next) { 76990075Sobrien 77090075Sobrien gothost = 0; 77190075Sobrien for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { 77290075Sobrien struct addrinfo *hi; 77390075Sobrien 77490075Sobrien for (hi = hrp->hostinfo; hi != NULL; 77552284Sobrien hi = hi->ai_next) 77690075Sobrien if (hi->ai_addrlen == ai->ai_addrlen && 77790075Sobrien memcmp(hi->ai_addr, 77890075Sobrien ai->ai_addr, 77990075Sobrien ai->ai_addr->sa_len) == 0) { 78090075Sobrien gothost++; 78190075Sobrien break; 78218334Speter } 78390075Sobrien if (gothost) 78490075Sobrien break; 78518334Speter } 78690075Sobrien if (hrp == NULL) { 78790075Sobrien if ((hrp = malloc(sizeof(struct ftphost))) == NULL) 78890075Sobrien goto nextline; 78990075Sobrien hrp->hostname = NULL; 79090075Sobrien insert = 1; 79190075Sobrien } else { 79290075Sobrien if (hrp->hostinfo && hrp->hostinfo != res) 79390075Sobrien freeaddrinfo(hrp->hostinfo); 79490075Sobrien insert = 0; /* host already in the chain */ 79590075Sobrien } 79690075Sobrien hrp->hostinfo = res; 79718334Speter 79890075Sobrien /* 79990075Sobrien * determine hostname to use. 80090075Sobrien * force defined name if there is a valid alias 80118334Speter * otherwise fallback to primary hostname 80290075Sobrien */ 80390075Sobrien /* XXX: getaddrinfo() can't do alias check */ 80490075Sobrien switch(hrp->hostinfo->ai_family) { 80590075Sobrien case AF_INET: 80618334Speter addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; 80718334Speter addrsize = sizeof(struct in_addr); 80818334Speter break; 80918334Speter case AF_INET6: 81018334Speter addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; 81118334Speter addrsize = sizeof(struct in6_addr); 81218334Speter break; 81318334Speter default: 81418334Speter /* should not reach here */ 81518334Speter freeaddrinfo(hrp->hostinfo); 81618334Speter if (insert) 81718334Speter free(hrp); /*not in chain, can free*/ 81818334Speter else 81918334Speter hrp->hostinfo = NULL; /*mark as blank*/ 82018334Speter goto nextline; 82118334Speter /* NOTREACHED */ 82218334Speter } 82318334Speter if ((hp = getipnodebyaddr(addr, addrsize, 82418334Speter hrp->hostinfo->ai_family, 82518334Speter &hp_error)) != NULL) { 82618334Speter if (strcmp(vhost, hp->h_name) != 0) { 82718334Speter if (hp->h_aliases == NULL) 82818334Speter vhost = hp->h_name; 82918334Speter else { 83018334Speter i = 0; 83118334Speter while (hp->h_aliases[i] && 83218334Speter strcmp(vhost, hp->h_aliases[i]) != 0) 83390075Sobrien ++i; 83490075Sobrien if (hp->h_aliases[i] == NULL) 83590075Sobrien vhost = hp->h_name; 83690075Sobrien } 83718334Speter } 83890075Sobrien } 83990075Sobrien if (hrp->hostname && 84090075Sobrien strcmp(hrp->hostname, vhost) != 0) { 84190075Sobrien free(hrp->hostname); 84290075Sobrien hrp->hostname = NULL; 84390075Sobrien } 84490075Sobrien if (hrp->hostname == NULL && 84590075Sobrien (hrp->hostname = strdup(vhost)) == NULL) { 84618334Speter freeaddrinfo(hrp->hostinfo); 84718334Speter hrp->hostinfo = NULL; /* mark as blank */ 84818334Speter if (hp) 84918334Speter freehostent(hp); 85018334Speter goto nextline; 85190075Sobrien } 85290075Sobrien hrp->anonuser = anonuser; 85390075Sobrien hrp->statfile = statfile; 85490075Sobrien hrp->welcome = welcome; 85590075Sobrien hrp->loginmsg = loginmsg; 85690075Sobrien if (insert) { 85790075Sobrien hrp->next = NULL; 85890075Sobrien lhrp->next = hrp; 85990075Sobrien lhrp = hrp; 86090075Sobrien } 86190075Sobrien if (hp) 86290075Sobrien freehostent(hp); 86390075Sobrien } 86490075Sobriennextline: 86590075Sobrien if (mp) 86690075Sobrien free(mp); 86790075Sobrien } 86890075Sobrien (void) fclose(fp); 86990075Sobrien } 87090075Sobrien} 87190075Sobrien 87290075Sobrienstatic void 87390075Sobrienselecthost(union sockunion *su) 87490075Sobrien{ 87590075Sobrien struct ftphost *hrp; 87690075Sobrien u_int16_t port; 87790075Sobrien#ifdef INET6 87890075Sobrien struct in6_addr *mapped_in6 = NULL; 87990075Sobrien#endif 88090075Sobrien struct addrinfo *hi; 88190075Sobrien 88290075Sobrien#ifdef INET6 88390075Sobrien /* 88490075Sobrien * XXX IPv4 mapped IPv6 addr consideraton, 88590075Sobrien * specified in rfc2373. 88690075Sobrien */ 88790075Sobrien if (su->su_family == AF_INET6 && 88890075Sobrien IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) 88990075Sobrien mapped_in6 = &su->su_sin6.sin6_addr; 89090075Sobrien#endif 89190075Sobrien 89290075Sobrien hrp = thishost = firsthost; /* default */ 89390075Sobrien port = su->su_port; 89490075Sobrien su->su_port = 0; 89590075Sobrien while (hrp != NULL) { 89690075Sobrien for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { 89790075Sobrien if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { 89890075Sobrien thishost = hrp; 89918334Speter goto found; 90018334Speter } 90118334Speter#ifdef INET6 90218334Speter /* XXX IPv4 mapped IPv6 addr consideraton */ 90318334Speter if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && 90418334Speter (memcmp(&mapped_in6->s6_addr[12], 90518334Speter &((struct sockaddr_in *)hi->ai_addr)->sin_addr, 90618334Speter sizeof(struct in_addr)) == 0)) { 90718334Speter thishost = hrp; 90818334Speter goto found; 90918334Speter } 91090075Sobrien#endif 91190075Sobrien } 91290075Sobrien hrp = hrp->next; 91390075Sobrien } 91418334Speterfound: 91518334Speter su->su_port = port; 91618334Speter /* setup static variables as appropriate */ 91718334Speter hostname = thishost->hostname; 91818334Speter ftpuser = thishost->anonuser; 91918334Speter} 92018334Speter#endif 92118334Speter 92218334Speter/* 92318334Speter * Helper function for sgetpwnam(). 92418334Speter */ 92518334Speterstatic char * 92618334Spetersgetsave(char *s) 92718334Speter{ 92818334Speter char *new = malloc(strlen(s) + 1); 92918334Speter 93018334Speter if (new == NULL) { 93118334Speter reply(421, "Ran out of memory."); 93290075Sobrien dologout(1); 93390075Sobrien /* NOTREACHED */ 93490075Sobrien } 93590075Sobrien (void) strcpy(new, s); 93690075Sobrien return (new); 93790075Sobrien} 93890075Sobrien 93990075Sobrien/* 94018334Speter * Save the result of a getpwnam. Used for USER command, since 94118334Speter * the data returned must not be clobbered by any other command 94218334Speter * (e.g., globbing). 94318334Speter * NB: The data returned by sgetpwnam() will remain valid until 94418334Speter * the next call to this function. Its difference from getpwnam() 94518334Speter * is that sgetpwnam() is known to be called from ftpd code only. 94618334Speter */ 94718334Speterstatic struct passwd * 94818334Spetersgetpwnam(char *name) 94918334Speter{ 95018334Speter static struct passwd save; 95118334Speter struct passwd *p; 95218334Speter 95318334Speter if ((p = getpwnam(name)) == NULL) 95418334Speter return (p); 95518334Speter if (save.pw_name) { 95618334Speter free(save.pw_name); 95718334Speter free(save.pw_passwd); 95818334Speter free(save.pw_gecos); 95918334Speter free(save.pw_dir); 96018334Speter free(save.pw_shell); 96118334Speter } 96218334Speter save = *p; 96318334Speter save.pw_name = sgetsave(p->pw_name); 96418334Speter save.pw_passwd = sgetsave(p->pw_passwd); 96518334Speter save.pw_gecos = sgetsave(p->pw_gecos); 96618334Speter save.pw_dir = sgetsave(p->pw_dir); 96718334Speter save.pw_shell = sgetsave(p->pw_shell); 96818334Speter return (&save); 96918334Speter} 97018334Speter 97118334Speterstatic int login_attempts; /* number of failed login attempts */ 97218334Speterstatic int askpasswd; /* had user command, ask for passwd */ 97318334Speterstatic char curname[MAXLOGNAME]; /* current USER name */ 97418334Speter 97518334Speter/* 97618334Speter * USER command. 97718334Speter * Sets global passwd pointer pw if named account exists and is acceptable; 97818334Speter * sets askpasswd if a PASS command is expected. If logged in previously, 97918334Speter * need to reset state. If name is "ftp" or "anonymous", the name is not in 98018334Speter * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 98118334Speter * If account doesn't exist, ask for passwd anyway. Otherwise, check user 98218334Speter * requesting login privileges. Disallow anyone who does not have a standard 98318334Speter * shell as returned by getusershell(). Disallow anyone mentioned in the file 98418334Speter * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 98518334Speter */ 98618334Spetervoid 98718334Speteruser(char *name) 98818334Speter{ 98918334Speter char *cp, *shell; 99018334Speter 99118334Speter if (logged_in) { 99218334Speter if (guest) { 99318334Speter reply(530, "Can't change user from guest login."); 99418334Speter return; 99518334Speter } else if (dochroot) { 99618334Speter reply(530, "Can't change user from chroot user."); 99718334Speter return; 99818334Speter } 99918334Speter end_login(); 100018334Speter } 100118334Speter 100218334Speter guest = 0; 100318334Speter#ifdef VIRTUAL_HOSTING 100418334Speter pw = sgetpwnam(thishost->anonuser); 100518334Speter#else 100618334Speter pw = sgetpwnam("ftp"); 100718334Speter#endif 100818334Speter if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 100918334Speter if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL) || 101018334Speter checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL)) 101118334Speter reply(530, "User %s access denied.", name); 101218334Speter else if (pw != NULL) { 101318334Speter guest = 1; 101418334Speter askpasswd = 1; 101590075Sobrien reply(331, 101690075Sobrien "Guest login ok, send your email address as password."); 101790075Sobrien } else 101890075Sobrien reply(530, "User %s unknown.", name); 101990075Sobrien if (!askpasswd && logging) 102090075Sobrien syslog(LOG_NOTICE, 102118334Speter "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 102218334Speter return; 102318334Speter } 102418334Speter if (anon_only != 0) { 102518334Speter reply(530, "Sorry, only anonymous ftp allowed."); 102690075Sobrien return; 102790075Sobrien } 102890075Sobrien 102990075Sobrien if ((pw = sgetpwnam(name))) { 103090075Sobrien if ((shell = pw->pw_shell) == NULL || *shell == 0) 103118334Speter shell = _PATH_BSHELL; 103290075Sobrien setusershell(); 103390075Sobrien while ((cp = getusershell()) != NULL) 103418334Speter if (strcmp(cp, shell) == 0) 103518334Speter break; 103618334Speter endusershell(); 103790075Sobrien 103890075Sobrien if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1, NULL)) { 103990075Sobrien reply(530, "User %s access denied.", name); 104090075Sobrien if (logging) 104190075Sobrien syslog(LOG_NOTICE, 104218334Speter "FTP LOGIN REFUSED FROM %s, %s", 104390075Sobrien remotehost, name); 104418334Speter pw = NULL; 104518334Speter return; 104618334Speter } 104790075Sobrien } 104890075Sobrien if (logging) 104990075Sobrien strncpy(curname, name, sizeof(curname)-1); 105090075Sobrien 105190075Sobrien pwok = 0; 105218334Speter#ifdef USE_PAM 105390075Sobrien /* XXX Kluge! The conversation mechanism needs to be fixed. */ 105418334Speter#endif 105518334Speter if (opiechallenge(&opiedata, name, opieprompt) == 0) { 105618334Speter pwok = (pw != NULL) && 105790075Sobrien opieaccessfile(remotehost) && 105890075Sobrien opiealways(pw->pw_dir); 105990075Sobrien reply(331, "Response to %s %s for %s.", 106090075Sobrien opieprompt, pwok ? "requested" : "required", name); 106190075Sobrien } else { 106290075Sobrien pwok = 1; 106390075Sobrien reply(331, "Password required for %s.", name); 106490075Sobrien } 106590075Sobrien askpasswd = 1; 106690075Sobrien /* 106790075Sobrien * Delay before reading passwd after first failed 106818334Speter * attempt to slow down passwd-guessing programs. 106918334Speter */ 107018334Speter if (login_attempts) 107118334Speter sleep(login_attempts); 107218334Speter} 107318334Speter 107418334Speter/* 107518334Speter * Check if a user is in the file "fname", 107618334Speter * return a pointer to a malloc'd string with the rest 107790075Sobrien * of the matching line in "residue" if not NULL. 107890075Sobrien */ 107990075Sobrienstatic int 108090075Sobriencheckuser(char *fname, char *name, int pwset, char **residue) 108190075Sobrien{ 108290075Sobrien FILE *fd; 108390075Sobrien int found = 0; 108450397Sobrien size_t len; 108550397Sobrien char *line, *mp, *p; 108618334Speter 108718334Speter if ((fd = fopen(fname, "r")) != NULL) { 108818334Speter while (!found && (line = fgetln(fd, &len)) != NULL) { 108918334Speter /* skip comments */ 109018334Speter if (line[0] == '#') 109118334Speter continue; 109218334Speter if (line[len - 1] == '\n') { 109318334Speter line[len - 1] = '\0'; 109418334Speter mp = NULL; 109518334Speter } else { 109618334Speter if ((mp = malloc(len + 1)) == NULL) 109718334Speter fatalerror("Ran out of memory."); 109818334Speter memcpy(mp, line, len); 109918334Speter mp[len] = '\0'; 110018334Speter line = mp; 110118334Speter } 1102107590Sobrien /* avoid possible leading and trailing whitespace */ 110318334Speter p = strtok(line, " \t"); 1104107590Sobrien /* skip empty lines */ 1105107590Sobrien if (p == NULL) 1106107590Sobrien goto nextline; 1107107590Sobrien /* 1108107590Sobrien * if first chr is '@', check group membership 1109107590Sobrien */ 1110107590Sobrien if (p[0] == '@') { 1111107590Sobrien int i = 0; 1112107590Sobrien struct group *grp; 1113107590Sobrien 1114107590Sobrien if (p[1] == '\0') /* single @ matches anyone */ 1115107590Sobrien found = 1; 1116107590Sobrien else { 1117107590Sobrien if ((grp = getgrnam(p+1)) == NULL) 111818334Speter goto nextline; 111990075Sobrien /* 112018334Speter * Check user's default group 112190075Sobrien */ 112290075Sobrien if (pwset && grp->gr_gid == pw->pw_gid) 112390075Sobrien found = 1; 112418334Speter /* 112518334Speter * Check supplementary groups 112618334Speter */ 112718334Speter while (!found && grp->gr_mem[i]) 112890075Sobrien found = strcmp(name, 112990075Sobrien grp->gr_mem[i++]) 113090075Sobrien == 0; 113190075Sobrien } 113218334Speter } 113390075Sobrien /* 113490075Sobrien * Otherwise, just check for username match 113590075Sobrien */ 113618334Speter else 113790075Sobrien found = strcmp(p, name) == 0; 113890075Sobrien /* 113990075Sobrien * Save the rest of line to "residue" if matched 114090075Sobrien */ 114190075Sobrien if (found && residue) { 114290075Sobrien if ((p = strtok(NULL, "")) != NULL) 114390075Sobrien p += strspn(p, " \t"); 114490075Sobrien if (p && *p) { 114590075Sobrien if ((*residue = strdup(p)) == NULL) 114618334Speter fatalerror("Ran out of memory."); 114718334Speter } else 114818334Speter *residue = NULL; 114990075Sobrien } 115090075Sobriennextline: 115118334Speter if (mp) 115290075Sobrien free(mp); 115390075Sobrien } 115490075Sobrien (void) fclose(fd); 115518334Speter } 115618334Speter return (found); 115718334Speter} 115818334Speter 115990075Sobrien/* 116018334Speter * Terminate login as previous user, if any, resetting state; 116118334Speter * used when USER command is given or login fails. 116218334Speter */ 116318334Speterstatic void 116418334Speterend_login(void) 116518334Speter{ 116618334Speter#ifdef USE_PAM 116718334Speter int e; 116818334Speter#endif 116918334Speter 117018334Speter (void) seteuid(0); 117118334Speter if (logged_in && dowtmp) 117218334Speter ftpd_logwtmp(ttyline, "", NULL); 117318334Speter pw = NULL; 117418334Speter#ifdef LOGIN_CAP 117518334Speter setusercontext(NULL, getpwuid(0), 0, 117618334Speter LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK| 117718334Speter LOGIN_SETMAC); 117818334Speter#endif 117918334Speter#ifdef USE_PAM 118018334Speter if (pamh) { 118118334Speter if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) 118218334Speter syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 118318334Speter if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) 118418334Speter syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); 118590075Sobrien if ((e = pam_end(pamh, e)) != PAM_SUCCESS) 118690075Sobrien syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 118790075Sobrien pamh = NULL; 118890075Sobrien } 118990075Sobrien#endif 119090075Sobrien logged_in = 0; 119190075Sobrien guest = 0; 119290075Sobrien dochroot = 0; 119390075Sobrien} 119490075Sobrien 119590075Sobrien#ifdef USE_PAM 119690075Sobrien 119790075Sobrien/* 119890075Sobrien * the following code is stolen from imap-uw PAM authentication module and 119990075Sobrien * login.c 120090075Sobrien */ 120190075Sobrien#define COPY_STRING(s) (s ? strdup(s) : NULL) 120290075Sobrien 120390075Sobrienstruct cred_t { 120490075Sobrien const char *uname; /* user name */ 120590075Sobrien const char *pass; /* password */ 120690075Sobrien}; 120790075Sobrientypedef struct cred_t cred_t; 120890075Sobrien 120990075Sobrienstatic int 121090075Sobrienauth_conv(int num_msg, const struct pam_message **msg, 121190075Sobrien struct pam_response **resp, void *appdata) 121290075Sobrien{ 121390075Sobrien int i; 121490075Sobrien cred_t *cred = (cred_t *) appdata; 121590075Sobrien struct pam_response *reply; 121690075Sobrien 121790075Sobrien reply = calloc(num_msg, sizeof *reply); 121890075Sobrien if (reply == NULL) 121990075Sobrien return PAM_BUF_ERR; 122090075Sobrien 122190075Sobrien for (i = 0; i < num_msg; i++) { 122290075Sobrien switch (msg[i]->msg_style) { 122390075Sobrien case PAM_PROMPT_ECHO_ON: /* assume want user name */ 122490075Sobrien reply[i].resp_retcode = PAM_SUCCESS; 122590075Sobrien reply[i].resp = COPY_STRING(cred->uname); 122690075Sobrien /* PAM frees resp. */ 122790075Sobrien break; 122890075Sobrien case PAM_PROMPT_ECHO_OFF: /* assume want password */ 122990075Sobrien reply[i].resp_retcode = PAM_SUCCESS; 123090075Sobrien reply[i].resp = COPY_STRING(cred->pass); 123190075Sobrien /* PAM frees resp. */ 123290075Sobrien break; 123390075Sobrien case PAM_TEXT_INFO: 123490075Sobrien case PAM_ERROR_MSG: 123590075Sobrien reply[i].resp_retcode = PAM_SUCCESS; 123690075Sobrien reply[i].resp = NULL; 123790075Sobrien break; 123890075Sobrien default: /* unknown message style */ 123990075Sobrien free(reply); 124090075Sobrien return PAM_CONV_ERR; 124190075Sobrien } 124290075Sobrien } 124390075Sobrien 124490075Sobrien *resp = reply; 124590075Sobrien return PAM_SUCCESS; 124690075Sobrien} 124752284Sobrien 124852284Sobrien/* 124952284Sobrien * Attempt to authenticate the user using PAM. Returns 0 if the user is 125052284Sobrien * authenticated, or 1 if not authenticated. If some sort of PAM system 125152284Sobrien * error occurs (e.g., the "/etc/pam.conf" file is missing) then this 125252284Sobrien * function returns -1. This can be used as an indication that we should 125390075Sobrien * fall back to a different authentication mechanism. 125452284Sobrien */ 125590075Sobrienstatic int 125652284Sobrienauth_pam(struct passwd **ppw, const char *pass) 125752284Sobrien{ 125852284Sobrien const char *tmpl_user; 125952284Sobrien const void *item; 126052284Sobrien int rval; 126152284Sobrien int e; 126252284Sobrien cred_t auth_cred = { (*ppw)->pw_name, pass }; 126352284Sobrien struct pam_conv conv = { &auth_conv, &auth_cred }; 126452284Sobrien 126552284Sobrien e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); 126652284Sobrien if (e != PAM_SUCCESS) { 126752284Sobrien /* 126852284Sobrien * In OpenPAM, it's OK to pass NULL to pam_strerror() 126952284Sobrien * if context creation has failed in the first place. 127052284Sobrien */ 127152284Sobrien syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); 127252284Sobrien return -1; 127352284Sobrien } 127452284Sobrien 127552284Sobrien e = pam_set_item(pamh, PAM_RHOST, remotehost); 127652284Sobrien if (e != PAM_SUCCESS) { 127790075Sobrien syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", 127890075Sobrien pam_strerror(pamh, e)); 127990075Sobrien if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 128052284Sobrien syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 128152284Sobrien } 128252284Sobrien pamh = NULL; 128352284Sobrien return -1; 128452284Sobrien } 128552284Sobrien 128690075Sobrien e = pam_authenticate(pamh, 0); 128790075Sobrien switch (e) { 128852284Sobrien case PAM_SUCCESS: 128952284Sobrien /* 129052284Sobrien * With PAM we support the concept of a "template" 129190075Sobrien * user. The user enters a login name which is 129290075Sobrien * authenticated by PAM, usually via a remote service 129390075Sobrien * such as RADIUS or TACACS+. If authentication 129490075Sobrien * succeeds, a different but related "template" name 129590075Sobrien * is used for setting the credentials, shell, and 129690075Sobrien * home directory. The name the user enters need only 129790075Sobrien * exist on the remote authentication server, but the 129890075Sobrien * template name must be present in the local password 129990075Sobrien * database. 130090075Sobrien * 130190075Sobrien * This is supported by two various mechanisms in the 130290075Sobrien * individual modules. However, from the application's 130390075Sobrien * point of view, the template user is always passed 130490075Sobrien * back as a changed value of the PAM_USER item. 130590075Sobrien */ 130690075Sobrien if ((e = pam_get_item(pamh, PAM_USER, &item)) == 130790075Sobrien PAM_SUCCESS) { 130890075Sobrien tmpl_user = (const char *) item; 130990075Sobrien if (strcmp((*ppw)->pw_name, tmpl_user) != 0) 131090075Sobrien *ppw = getpwnam(tmpl_user); 131190075Sobrien } else 131290075Sobrien syslog(LOG_ERR, "Couldn't get PAM_USER: %s", 131390075Sobrien pam_strerror(pamh, e)); 131452284Sobrien rval = 0; 131552284Sobrien break; 131690075Sobrien 131790075Sobrien case PAM_AUTH_ERR: 131890075Sobrien case PAM_USER_UNKNOWN: 131990075Sobrien case PAM_MAXTRIES: 132090075Sobrien rval = 1; 132190075Sobrien break; 132290075Sobrien 132390075Sobrien default: 132490075Sobrien syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); 132590075Sobrien rval = -1; 132690075Sobrien break; 132790075Sobrien } 132890075Sobrien 132990075Sobrien if (rval == 0) { 133090075Sobrien e = pam_acct_mgmt(pamh, 0); 133190075Sobrien if (e != PAM_SUCCESS) { 133290075Sobrien syslog(LOG_ERR, "pam_acct_mgmt: %s", 133318334Speter pam_strerror(pamh, e)); 133418334Speter rval = 1; 133518334Speter } 133618334Speter } 133718334Speter 133818334Speter if (rval != 0) { 133918334Speter if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { 134090075Sobrien syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); 134118334Speter } 134290075Sobrien pamh = NULL; 134390075Sobrien } 134490075Sobrien return rval; 134590075Sobrien} 134690075Sobrien 134790075Sobrien#endif /* USE_PAM */ 134890075Sobrien 134918334Spetervoid 135018334Speterpass(char *passwd) 135118334Speter{ 135218334Speter int rval; 135318334Speter FILE *fd; 135418334Speter#ifdef LOGIN_CAP 135518334Speter login_cap_t *lc = NULL; 135618334Speter#endif 135718334Speter#ifdef USE_PAM 135818334Speter int e; 135918334Speter#endif 136018334Speter char *residue = NULL; 136118334Speter char *xpasswd; 136218334Speter 136318334Speter if (logged_in || askpasswd == 0) { 136418334Speter reply(503, "Login with USER first."); 136518334Speter return; 136618334Speter } 136752284Sobrien askpasswd = 0; 136818334Speter if (!guest) { /* "ftp" is only account allowed no password */ 136918334Speter if (pw == NULL) { 137018334Speter rval = 1; /* failure below */ 137118334Speter goto skip; 137218334Speter } 137318334Speter#ifdef USE_PAM 137418334Speter rval = auth_pam(&pw, passwd); 137518334Speter if (rval >= 0) { 137618334Speter opieunlock(); 137718334Speter goto skip; 137818334Speter } 137918334Speter#endif 138018334Speter if (opieverify(&opiedata, passwd) == 0) 138118334Speter xpasswd = pw->pw_passwd; 138218334Speter else if (pwok) { 138390075Sobrien xpasswd = crypt(passwd, pw->pw_passwd); 138490075Sobrien if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') 138590075Sobrien xpasswd = ":"; 138690075Sobrien } else { 138718334Speter rval = 1; 138818334Speter goto skip; 138918334Speter } 139018334Speter rval = strcmp(pw->pw_passwd, xpasswd); 139118334Speter if (pw->pw_expire && time(NULL) >= pw->pw_expire) 139290075Sobrien rval = 1; /* failure */ 139390075Sobrienskip: 139450397Sobrien /* 139518334Speter * If rval == 1, the user failed the authentication check 139618334Speter * above. If rval == 0, either PAM or local authentication 139718334Speter * succeeded. 139818334Speter */ 139918334Speter if (rval) { 140018334Speter reply(530, "Login incorrect."); 140118334Speter if (logging) { 140218334Speter syslog(LOG_NOTICE, 140318334Speter "FTP LOGIN FAILED FROM %s", 140418334Speter remotehost); 140518334Speter syslog(LOG_AUTHPRIV | LOG_NOTICE, 140618334Speter "FTP LOGIN FAILED FROM %s, %s", 140750397Sobrien remotehost, curname); 140850397Sobrien } 140950397Sobrien pw = NULL; 141050397Sobrien if (login_attempts++ >= 5) { 141118334Speter syslog(LOG_NOTICE, 141218334Speter "repeated login failures from %s", 141318334Speter remotehost); 141418334Speter exit(0); 141518334Speter } 141618334Speter return; 141718334Speter } 141818334Speter } 141918334Speter login_attempts = 0; /* this time successful */ 142018334Speter if (setegid(pw->pw_gid) < 0) { 142118334Speter reply(550, "Can't set gid."); 142218334Speter return; 142318334Speter } 142418334Speter /* May be overridden by login.conf */ 142518334Speter (void) umask(defumask); 142618334Speter#ifdef LOGIN_CAP 142718334Speter if ((lc = login_getpwclass(pw)) != NULL) { 142818334Speter char remote_ip[NI_MAXHOST]; 142918334Speter 143018334Speter if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 143118334Speter remote_ip, sizeof(remote_ip) - 1, NULL, 0, 143218334Speter NI_NUMERICHOST)) 143318334Speter *remote_ip = 0; 143418334Speter remote_ip[sizeof(remote_ip) - 1] = 0; 143518334Speter if (!auth_hostok(lc, remotehost, remote_ip)) { 143618334Speter syslog(LOG_INFO|LOG_AUTH, 143718334Speter "FTP LOGIN FAILED (HOST) as %s: permission denied.", 143818334Speter pw->pw_name); 143918334Speter reply(530, "Permission denied."); 144018334Speter pw = NULL; 144190075Sobrien return; 144218334Speter } 144390075Sobrien if (!auth_timeok(lc, time(NULL))) { 144490075Sobrien reply(530, "Login not available right now."); 144590075Sobrien pw = NULL; 144618334Speter return; 144790075Sobrien } 144818334Speter } 144990075Sobrien setusercontext(lc, pw, 0, 145090075Sobrien LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| 145190075Sobrien LOGIN_SETRESOURCES|LOGIN_SETUMASK|LOGIN_SETMAC); 145218334Speter#else 145318334Speter setlogin(pw->pw_name); 145418334Speter (void) initgroups(pw->pw_name, pw->pw_gid); 145518334Speter#endif 145618334Speter 145718334Speter#ifdef USE_PAM 145818334Speter if (pamh) { 145918334Speter if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { 146018334Speter syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); 146118334Speter } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 146218334Speter syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); 146318334Speter } 146418334Speter } 146518334Speter#endif 146690075Sobrien 146790075Sobrien /* open wtmp before chroot */ 146890075Sobrien if (dowtmp) 146990075Sobrien ftpd_logwtmp(ttyline, pw->pw_name, 147090075Sobrien (struct sockaddr *)&his_addr); 147190075Sobrien logged_in = 1; 147290075Sobrien 147318334Speter if (guest && stats && statfd < 0) 147490075Sobrien#ifdef VIRTUAL_HOSTING 147590075Sobrien statfd = open(thishost->statfile, O_WRONLY|O_APPEND); 147618334Speter#else 147718334Speter statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); 147818334Speter#endif 147918334Speter if (statfd < 0) 148018334Speter stats = 0; 148118334Speter 148290075Sobrien dochroot = 148390075Sobrien checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue) 148490075Sobrien#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ 148590075Sobrien || login_getcapbool(lc, "ftp-chroot", 0) 148690075Sobrien#endif 148790075Sobrien ; 148818334Speter chrootdir = NULL; 148918334Speter /* 149018334Speter * For a chrooted local user, 149118334Speter * a) see whether ftpchroot(5) specifies a chroot directory, 149218334Speter * b) extract the directory pathname from the line, 149318334Speter * c) expand it to the absolute pathname if necessary. 149418334Speter */ 149518334Speter if (dochroot && residue && 149650397Sobrien (chrootdir = strtok(residue, " \t")) != NULL) { 149750397Sobrien if (chrootdir[0] != '/') 149818334Speter asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); 149918334Speter else 150018334Speter chrootdir = strdup(chrootdir); /* make it permanent */ 150118334Speter if (chrootdir == NULL) 150218334Speter fatalerror("Ran out of memory."); 150390075Sobrien } 150418334Speter if (guest || dochroot) { 150590075Sobrien /* 150690075Sobrien * If no chroot directory set yet, use the login directory. 150790075Sobrien * Copy it so it can be modified while pw->pw_dir stays intact. 150890075Sobrien */ 150990075Sobrien if (chrootdir == NULL && 151090075Sobrien (chrootdir = strdup(pw->pw_dir)) == NULL) 151190075Sobrien fatalerror("Ran out of memory."); 151218334Speter /* 151318334Speter * Check for the "/chroot/./home" syntax, 151418334Speter * separate the chroot and home directory pathnames. 151518334Speter */ 151618334Speter if ((homedir = strstr(chrootdir, "/./")) != NULL) { 151718334Speter *(homedir++) = '\0'; /* wipe '/' */ 151818334Speter homedir++; /* skip '.' */ 151918334Speter } else { 152018334Speter /* 152150397Sobrien * We MUST do a chdir() after the chroot. Otherwise 152250397Sobrien * the old current directory will be accessible as "." 152350397Sobrien * outside the new root! 152418334Speter */ 152550397Sobrien homedir = "/"; 152690075Sobrien } 152750397Sobrien /* 152850397Sobrien * Finally, do chroot() 152990075Sobrien */ 153050397Sobrien if (chroot(chrootdir) < 0) { 153150397Sobrien reply(550, "Can't change root."); 153218334Speter goto bad; 153318334Speter } 153418334Speter } else /* real user w/o chroot */ 153518334Speter homedir = pw->pw_dir; 153618334Speter /* 153718334Speter * Set euid *before* doing chdir() so 153890075Sobrien * a) the user won't be carried to a directory that he couldn't reach 153918334Speter * on his own due to no permission to upper path components, 154018334Speter * b) NFS mounted homedirs w/restrictive permissions will be accessible 154190075Sobrien * (uid 0 has no root power over NFS if not mapped explicitly.) 154218334Speter */ 154390075Sobrien if (seteuid(pw->pw_uid) < 0) { 154418334Speter reply(550, "Can't set uid."); 154518334Speter goto bad; 154618334Speter } 154718334Speter if (chdir(homedir) < 0) { 154818334Speter if (guest || dochroot) { 154918334Speter reply(550, "Can't change to base directory."); 155018334Speter goto bad; 155118334Speter } else { 155218334Speter if (chdir("/") < 0) { 155318334Speter reply(550, "Root is inaccessible."); 155418334Speter goto bad; 155518334Speter } 155690075Sobrien lreply(230, "No directory! Logging in with home=/."); 155718334Speter } 155818334Speter } 155918334Speter 156018334Speter /* 156118334Speter * Display a login message, if it exists. 156290075Sobrien * N.B. reply(230,) must follow the message. 156390075Sobrien */ 156490075Sobrien#ifdef VIRTUAL_HOSTING 156590075Sobrien fd = fopen(thishost->loginmsg, "r"); 156690075Sobrien#else 156790075Sobrien fd = fopen(_PATH_FTPLOGINMESG, "r"); 156890075Sobrien#endif 156990075Sobrien if (fd != NULL) { 157090075Sobrien char *cp, line[LINE_MAX]; 157190075Sobrien 157290075Sobrien while (fgets(line, sizeof(line), fd) != NULL) { 157390075Sobrien if ((cp = strchr(line, '\n')) != NULL) 157490075Sobrien *cp = '\0'; 157590075Sobrien lreply(230, "%s", line); 157690075Sobrien } 157790075Sobrien (void) fflush(stdout); 157890075Sobrien (void) fclose(fd); 157990075Sobrien } 158090075Sobrien if (guest) { 158190075Sobrien if (ident != NULL) 158290075Sobrien free(ident); 158390075Sobrien ident = strdup(passwd); 158490075Sobrien if (ident == NULL) 158590075Sobrien fatalerror("Ran out of memory."); 158690075Sobrien 158790075Sobrien reply(230, "Guest login ok, access restrictions apply."); 158890075Sobrien#ifdef SETPROCTITLE 158990075Sobrien#ifdef VIRTUAL_HOSTING 159090075Sobrien if (thishost != firsthost) 159190075Sobrien snprintf(proctitle, sizeof(proctitle), 159290075Sobrien "%s: anonymous(%s)/%s", remotehost, hostname, 159390075Sobrien passwd); 159490075Sobrien else 159590075Sobrien#endif 159690075Sobrien snprintf(proctitle, sizeof(proctitle), 159790075Sobrien "%s: anonymous/%s", remotehost, passwd); 159890075Sobrien setproctitle("%s", proctitle); 159990075Sobrien#endif /* SETPROCTITLE */ 160090075Sobrien if (logging) 160190075Sobrien syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 160290075Sobrien remotehost, passwd); 160390075Sobrien } else { 160490075Sobrien if (dochroot) 160590075Sobrien reply(230, "User %s logged in, " 160690075Sobrien "access restrictions apply.", pw->pw_name); 160790075Sobrien else 160890075Sobrien reply(230, "User %s logged in.", pw->pw_name); 160990075Sobrien 161090075Sobrien#ifdef SETPROCTITLE 161190075Sobrien snprintf(proctitle, sizeof(proctitle), 161290075Sobrien "%s: user/%s", remotehost, pw->pw_name); 161390075Sobrien setproctitle("%s", proctitle); 161490075Sobrien#endif /* SETPROCTITLE */ 161590075Sobrien if (logging) 161690075Sobrien syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 161790075Sobrien remotehost, pw->pw_name); 161890075Sobrien } 161990075Sobrien if (logging && (guest || dochroot)) 162090075Sobrien syslog(LOG_INFO, "session root changed to %s", chrootdir); 162190075Sobrien#ifdef LOGIN_CAP 162290075Sobrien login_close(lc); 162390075Sobrien#endif 162490075Sobrien if (residue) 162590075Sobrien free(residue); 162690075Sobrien return; 162790075Sobrienbad: 162890075Sobrien /* Forget all about it... */ 162990075Sobrien#ifdef LOGIN_CAP 163090075Sobrien login_close(lc); 163190075Sobrien#endif 163290075Sobrien if (residue) 163390075Sobrien free(residue); 163490075Sobrien end_login(); 163590075Sobrien} 163690075Sobrien 163790075Sobrienvoid 163890075Sobrienretrieve(char *cmd, char *name) 163990075Sobrien{ 164090075Sobrien FILE *fin, *dout; 164190075Sobrien struct stat st; 164290075Sobrien int (*closefunc)(FILE *); 164390075Sobrien time_t start; 164490075Sobrien 164590075Sobrien if (cmd == 0) { 164690075Sobrien fin = fopen(name, "r"), closefunc = fclose; 164790075Sobrien st.st_size = 0; 164890075Sobrien } else { 164990075Sobrien char line[BUFSIZ]; 165090075Sobrien 165190075Sobrien (void) snprintf(line, sizeof(line), cmd, name), name = line; 165290075Sobrien fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 165390075Sobrien st.st_size = -1; 165490075Sobrien st.st_blksize = BUFSIZ; 165590075Sobrien } 165690075Sobrien if (fin == NULL) { 165790075Sobrien if (errno != 0) { 165890075Sobrien perror_reply(550, name); 165990075Sobrien if (cmd == 0) { 166090075Sobrien LOGCMD("get", name); 166190075Sobrien } 166290075Sobrien } 166390075Sobrien return; 166490075Sobrien } 166590075Sobrien byte_count = -1; 166690075Sobrien if (cmd == 0) { 166790075Sobrien if (fstat(fileno(fin), &st) < 0) { 166890075Sobrien perror_reply(550, name); 166990075Sobrien goto done; 167090075Sobrien } 167190075Sobrien if (!S_ISREG(st.st_mode)) { 167290075Sobrien /* 167390075Sobrien * Never sending a raw directory is a workaround 167490075Sobrien * for buggy clients that will attempt to RETR 167590075Sobrien * a directory before listing it, e.g., Mozilla. 167690075Sobrien * Preventing a guest from getting irregular files 167790075Sobrien * is a simple security measure. 167890075Sobrien */ 167990075Sobrien if (S_ISDIR(st.st_mode) || guest) { 168090075Sobrien reply(550, "%s: not a plain file.", name); 168190075Sobrien goto done; 168290075Sobrien } 168390075Sobrien st.st_size = -1; 168490075Sobrien /* st.st_blksize is set for all descriptor types */ 168590075Sobrien } 168690075Sobrien } 168790075Sobrien if (restart_point) { 168890075Sobrien if (type == TYPE_A) { 168990075Sobrien off_t i, n; 169090075Sobrien int c; 169118334Speter 169218334Speter n = restart_point; 169318334Speter i = 0; 169418334Speter while (i++ < n) { 169518334Speter if ((c=getc(fin)) == EOF) { 169618334Speter perror_reply(550, name); 169718334Speter goto done; 169818334Speter } 169918334Speter if (c == '\n') 170018334Speter i++; 170118334Speter } 170218334Speter } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 170318334Speter perror_reply(550, name); 170418334Speter goto done; 170518334Speter } 170618334Speter } 170718334Speter dout = dataconn(name, st.st_size, "w"); 170818334Speter if (dout == NULL) 170918334Speter goto done; 171018334Speter time(&start); 171118334Speter send_data(fin, dout, st.st_blksize, st.st_size, 171218334Speter restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); 171318334Speter if (cmd == 0 && guest && stats && byte_count > 0) 171418334Speter logxfer(name, byte_count, start); 171518334Speter (void) fclose(dout); 171618334Speter data = -1; 171718334Speter pdata = -1; 171818334Speterdone: 171918334Speter if (cmd == 0) 172018334Speter LOGBYTES("get", name, byte_count); 172118334Speter (*closefunc)(fin); 172218334Speter} 172318334Speter 172490075Sobrienvoid 172518334Speterstore(char *name, char *mode, int unique) 172618334Speter{ 172718334Speter int fd; 172850397Sobrien FILE *fout, *din; 172950397Sobrien int (*closefunc)(FILE *); 173018334Speter 173118334Speter if (*mode == 'a') { /* APPE */ 173218334Speter if (unique) { 173318334Speter /* Programming error */ 173418334Speter syslog(LOG_ERR, "Internal: unique flag to APPE"); 173518334Speter unique = 0; 173618334Speter } 173718334Speter if (guest && noguestmod) { 173818334Speter reply(550, "Appending to existing file denied."); 173918334Speter goto err; 174018334Speter } 174118334Speter restart_point = 0; /* not affected by preceding REST */ 174218334Speter } 174390075Sobrien if (unique) /* STOU overrides REST */ 174418334Speter restart_point = 0; 174518334Speter if (guest && noguestmod) { 174690075Sobrien if (restart_point) { /* guest STOR w/REST */ 174718334Speter reply(550, "Modifying existing file denied."); 174818334Speter goto err; 174990075Sobrien } else /* treat guest STOR as STOU */ 175090075Sobrien unique = 1; 175118334Speter } 175290075Sobrien 175318334Speter if (restart_point) 175490075Sobrien mode = "r+"; /* so ASCII manual seek can work */ 175590075Sobrien if (unique) { 175690075Sobrien if ((fd = guniquefd(name, &name)) < 0) 175718334Speter goto err; 175890075Sobrien fout = fdopen(fd, mode); 175990075Sobrien } else 176018334Speter fout = fopen(name, mode); 176190075Sobrien closefunc = fclose; 176290075Sobrien if (fout == NULL) { 176390075Sobrien perror_reply(553, name); 176418334Speter goto err; 176518334Speter } 176618334Speter byte_count = -1; 176718334Speter if (restart_point) { 176818334Speter if (type == TYPE_A) { 176918334Speter off_t i, n; 177018334Speter int c; 177118334Speter 177290075Sobrien n = restart_point; 177352284Sobrien i = 0; 177418334Speter while (i++ < n) { 177518334Speter if ((c=getc(fout)) == EOF) { 177618334Speter perror_reply(550, name); 177718334Speter goto done; 177890075Sobrien } 177918334Speter if (c == '\n') 178018334Speter i++; 178118334Speter } 178218334Speter /* 178318334Speter * We must do this seek to "current" position 178452284Sobrien * because we are changing from reading to 178552284Sobrien * writing. 178618334Speter */ 178790075Sobrien if (fseeko(fout, 0, SEEK_CUR) < 0) { 178852284Sobrien perror_reply(550, name); 178952284Sobrien goto done; 179052284Sobrien } 179152284Sobrien } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 179290075Sobrien perror_reply(550, name); 179352284Sobrien goto done; 179452284Sobrien } 179552284Sobrien } 179652284Sobrien din = dataconn(name, -1, "r"); 179752284Sobrien if (din == NULL) 179852284Sobrien goto done; 179952284Sobrien if (receive_data(din, fout) == 0) { 180052284Sobrien if (unique) 180152284Sobrien reply(226, "Transfer complete (unique file name:%s).", 180252284Sobrien name); 180352284Sobrien else 180452284Sobrien reply(226, "Transfer complete."); 180552284Sobrien } 180652284Sobrien (void) fclose(din); 180752284Sobrien data = -1; 180890075Sobrien pdata = -1; 180952284Sobriendone: 181052284Sobrien LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); 181190075Sobrien (*closefunc)(fout); 181252284Sobrien return; 181352284Sobrienerr: 181452284Sobrien LOGCMD(*mode == 'a' ? "append" : "put" , name); 181518334Speter return; 181618334Speter} 181718334Speter 181818334Speterstatic FILE * 181918334Spetergetdatasock(char *mode) 182018334Speter{ 182118334Speter int on = 1, s, t, tries; 182218334Speter 182318334Speter if (data >= 0) 182418334Speter return (fdopen(data, mode)); 182518334Speter 182690075Sobrien s = socket(data_dest.su_family, SOCK_STREAM, 0); 182790075Sobrien if (s < 0) 182818334Speter goto bad; 182918334Speter if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 183018334Speter syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); 183118334Speter /* anchor socket to avoid multi-homing problems */ 183218334Speter data_source = ctrl_addr; 183390075Sobrien data_source.su_port = htons(dataport); 183490075Sobrien (void) seteuid(0); 183590075Sobrien for (tries = 1; ; tries++) { 183690075Sobrien /* 183790075Sobrien * We should loop here since it's possible that 183890075Sobrien * another ftpd instance has passed this point and is 183990075Sobrien * trying to open a data connection in active mode now. 184090075Sobrien * Until the other connection is opened, we'll be getting 184190075Sobrien * EADDRINUSE because no SOCK_STREAM sockets in the system 184218334Speter * can share both local and remote addresses, localIP:20 184318334Speter * and *:* in this case. 184418334Speter */ 184518334Speter if (bind(s, (struct sockaddr *)&data_source, 184618334Speter data_source.su_len) >= 0) 184718334Speter break; 184818334Speter if (errno != EADDRINUSE || tries > 10) 184918334Speter goto bad; 185018334Speter sleep(tries); 185118334Speter } 185218334Speter (void) seteuid(pw->pw_uid); 185318334Speter#ifdef IP_TOS 185490075Sobrien if (data_source.su_family == AF_INET) 185518334Speter { 185618334Speter on = IPTOS_THROUGHPUT; 185718334Speter if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) 185818334Speter syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); 185918334Speter } 186090075Sobrien#endif 186118334Speter#ifdef TCP_NOPUSH 186218334Speter /* 186318334Speter * Turn off push flag to keep sender TCP from sending short packets 186418334Speter * at the boundaries of each write(). Should probably do a SO_SNDBUF 186518334Speter * to set the send buffer size as well, but that may not be desirable 186618334Speter * in heavy-load situations. 186718334Speter */ 186818334Speter on = 1; 186918334Speter if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) 187018334Speter syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); 187118334Speter#endif 187218334Speter#ifdef SO_SNDBUF 187318334Speter on = 65536; 187418334Speter if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof on) < 0) 187518334Speter syslog(LOG_WARNING, "data setsockopt (SO_SNDBUF): %m"); 187618334Speter#endif 187790075Sobrien 187818334Speter return (fdopen(s, mode)); 187918334Speterbad: 188018334Speter /* Return the real value of errno (close may change it) */ 188118334Speter t = errno; 188218334Speter (void) seteuid(pw->pw_uid); 188318334Speter (void) close(s); 188418334Speter errno = t; 188518334Speter return (NULL); 188618334Speter} 188718334Speter 188818334Speterstatic FILE * 188918334Speterdataconn(char *name, off_t size, char *mode) 189018334Speter{ 189118334Speter char sizebuf[32]; 189218334Speter FILE *file; 189318334Speter int retry = 0, tos, conerrno; 189418334Speter 189518334Speter file_size = size; 189618334Speter byte_count = 0; 189718334Speter if (size != -1) 189818334Speter (void) snprintf(sizebuf, sizeof(sizebuf), 189918334Speter " (%jd bytes)", (intmax_t)size); 190018334Speter else 190118334Speter *sizebuf = '\0'; 190218334Speter if (pdata >= 0) { 190390075Sobrien union sockunion from; 190418334Speter socklen_t fromlen = ctrl_addr.su_len; 190590075Sobrien int flags, s; 190618334Speter struct timeval timeout; 190718334Speter fd_set set; 190818334Speter 190990075Sobrien FD_ZERO(&set); 191090075Sobrien FD_SET(pdata, &set); 191118334Speter 191218334Speter timeout.tv_usec = 0; 191318334Speter timeout.tv_sec = 120; 191418334Speter 191518334Speter /* 191618334Speter * Granted a socket is in the blocking I/O mode, 191718334Speter * accept() will block after a successful select() 191818334Speter * if the selected connection dies in between. 191918334Speter * Therefore set the non-blocking I/O flag here. 192018334Speter */ 192118334Speter if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 192218334Speter fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) 192318334Speter goto pdata_err; 192418334Speter if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || 192518334Speter (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) 192618334Speter goto pdata_err; 192718334Speter (void) close(pdata); 192818334Speter pdata = s; 192918334Speter /* 193018334Speter * Unset the inherited non-blocking I/O flag 193118334Speter * on the child socket so stdio can work on it. 193218334Speter */ 193318334Speter if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || 193418334Speter fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) 193518334Speter goto pdata_err; 193618334Speter#ifdef IP_TOS 193790075Sobrien if (from.su_family == AF_INET) 193890075Sobrien { 193918334Speter tos = IPTOS_THROUGHPUT; 194090075Sobrien if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) 194190075Sobrien syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); 194290075Sobrien } 194390075Sobrien#endif 194490075Sobrien reply(150, "Opening %s mode data connection for '%s'%s.", 194590075Sobrien type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 194690075Sobrien return (fdopen(pdata, mode)); 194796263Sobrienpdata_err: 194896263Sobrien reply(425, "Can't open data connection."); 194918334Speter (void) close(pdata); 195090075Sobrien pdata = -1; 195190075Sobrien return (NULL); 195296263Sobrien } 195318334Speter if (data >= 0) { 195490075Sobrien reply(125, "Using existing data connection for '%s'%s.", 195590075Sobrien name, sizebuf); 195618334Speter usedefault = 1; 195790075Sobrien return (fdopen(data, mode)); 195890075Sobrien } 195990075Sobrien if (usedefault) 196090075Sobrien data_dest = his_addr; 196150397Sobrien usedefault = 1; 196296263Sobrien do { 196390075Sobrien file = getdatasock(mode); 196496263Sobrien if (file == NULL) { 196596263Sobrien char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; 196696263Sobrien 196750397Sobrien if (getnameinfo((struct sockaddr *)&data_source, 196890075Sobrien data_source.su_len, 196990075Sobrien hostbuf, sizeof(hostbuf) - 1, 197090075Sobrien portbuf, sizeof(portbuf) - 1, 197190075Sobrien NI_NUMERICHOST|NI_NUMERICSERV)) 197290075Sobrien *hostbuf = *portbuf = 0; 197350397Sobrien hostbuf[sizeof(hostbuf) - 1] = 0; 197490075Sobrien portbuf[sizeof(portbuf) - 1] = 0; 197596263Sobrien reply(425, "Can't create data socket (%s,%s): %s.", 197696263Sobrien hostbuf, portbuf, strerror(errno)); 197796263Sobrien return (NULL); 197896263Sobrien } 197996263Sobrien data = fileno(file); 198096263Sobrien conerrno = 0; 198196263Sobrien if (connect(data, (struct sockaddr *)&data_dest, 198296263Sobrien data_dest.su_len) == 0) 198350397Sobrien break; 198490075Sobrien conerrno = errno; 198590075Sobrien (void) fclose(file); 198690075Sobrien data = -1; 198790075Sobrien if (conerrno == EADDRINUSE) { 198818334Speter sleep(swaitint); 198990075Sobrien retry += swaitint; 199090075Sobrien } else { 199190075Sobrien break; 199218334Speter } 199390075Sobrien } while (retry <= swaitmax); 199490075Sobrien if (conerrno != 0) { 199590075Sobrien reply(425, "Can't build data connection: %s.", 199690075Sobrien strerror(conerrno)); 199718334Speter return (NULL); 199818334Speter } 199990075Sobrien reply(150, "Opening %s mode data connection for '%s'%s.", 200090075Sobrien type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 200190075Sobrien return (file); 200290075Sobrien} 200318334Speter 200490075Sobrien/* 200590075Sobrien * A helper macro to avoid code duplication 200690075Sobrien * in send_data() and receive_data(). 200790075Sobrien * 200890075Sobrien * XXX We have to block SIGURG during putc() because BSD stdio 200990075Sobrien * is unable to restart interrupted write operations and hence 201090075Sobrien * the entire buffer contents will be lost as soon as a write() 201190075Sobrien * call indicates EINTR to stdio. 201290075Sobrien */ 201318334Speter#define FTPD_PUTC(ch, file, label) \ 201490075Sobrien do { \ 201590075Sobrien int ret; \ 201690075Sobrien \ 201790075Sobrien do { \ 201890075Sobrien START_UNSAFE; \ 201918334Speter ret = putc((ch), (file)); \ 202090075Sobrien END_UNSAFE; \ 202190075Sobrien CHECKOOB(return (-1)) \ 202290075Sobrien else if (ferror(file)) \ 202390075Sobrien goto label; \ 202490075Sobrien clearerr(file); \ 202590075Sobrien } while (ret == EOF); \ 202618334Speter } while (0) 202790075Sobrien 202890075Sobrien/* 202990075Sobrien * Tranfer the contents of "instr" to "outstr" peer using the appropriate 203090075Sobrien * encapsulation of the data subject to Mode, Structure, and Type. 203190075Sobrien * 203290075Sobrien * NB: Form isn't handled. 203318334Speter */ 203418334Speterstatic int 203518334Spetersend_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg) 203618334Speter{ 203718334Speter int c, cp, filefd, netfd; 203818334Speter char *buf; 203990075Sobrien 204090075Sobrien STARTXFER; 204118334Speter 204290075Sobrien switch (type) { 204318334Speter 204490075Sobrien case TYPE_A: 204518334Speter cp = EOF; 204618334Speter for (;;) { 204790075Sobrien c = getc(instr); 204818334Speter CHECKOOB(return (-1)) 204990075Sobrien else if (c == EOF && ferror(instr)) 205090075Sobrien goto file_err; 205190075Sobrien if (c == EOF) { 205290075Sobrien if (ferror(instr)) { /* resume after OOB */ 205390075Sobrien clearerr(instr); 205490075Sobrien continue; 205590075Sobrien } 205690075Sobrien if (feof(instr)) /* EOF */ 205790075Sobrien break; 205890075Sobrien syslog(LOG_ERR, "Internal: impossible condition" 205990075Sobrien " on file after getc()"); 206090075Sobrien goto file_err; 206190075Sobrien } 206290075Sobrien if (c == '\n' && cp != '\r') { 206390075Sobrien FTPD_PUTC('\r', outstr, data_err); 206490075Sobrien byte_count++; 206590075Sobrien } 206690075Sobrien FTPD_PUTC(c, outstr, data_err); 206790075Sobrien byte_count++; 206818334Speter cp = c; 206918334Speter } 207090075Sobrien#ifdef notyet /* BSD stdio isn't ready for that */ 207190075Sobrien while (fflush(outstr) == EOF) { 207290075Sobrien CHECKOOB(return (-1)) 207390075Sobrien else 207490075Sobrien goto data_err; 207590075Sobrien clearerr(outstr); 207690075Sobrien } 207790075Sobrien ENDXFER; 207890075Sobrien#else 207990075Sobrien ENDXFER; 208090075Sobrien if (fflush(outstr) == EOF) 208190075Sobrien goto data_err; 208290075Sobrien#endif 208390075Sobrien reply(226, "Transfer complete."); 208490075Sobrien return (0); 208590075Sobrien 208690075Sobrien case TYPE_I: 208790075Sobrien case TYPE_L: 208890075Sobrien /* 208990075Sobrien * isreg is only set if we are not doing restart and we 209090075Sobrien * are sending a regular file 209190075Sobrien */ 209290075Sobrien netfd = fileno(outstr); 209390075Sobrien filefd = fileno(instr); 209418334Speter 209518334Speter if (isreg) { 209618334Speter char *msg = "Transfer complete."; 209790075Sobrien off_t cnt, offset; 209890075Sobrien int err; 209918334Speter 210018334Speter cnt = offset = 0; 210190075Sobrien 210218334Speter while (filesize > 0) { 210390075Sobrien err = sendfile(filefd, netfd, offset, 0, 210418334Speter NULL, &cnt, 0); 210590075Sobrien /* 210618334Speter * Calculate byte_count before OOB processing. 210718334Speter * It can be used in myoob() later. 210818334Speter */ 210918334Speter byte_count += cnt; 211090075Sobrien offset += cnt; 211118334Speter filesize -= cnt; 211218334Speter CHECKOOB(return (-1)) 211318334Speter else if (err == -1) { 211418334Speter if (errno != EINTR && 211518334Speter cnt == 0 && offset == 0) 211618334Speter goto oldway; 211790075Sobrien goto data_err; 211890075Sobrien } 211990075Sobrien if (err == -1) /* resume after OOB */ 212090075Sobrien continue; 212190075Sobrien /* 212290075Sobrien * We hit the EOF prematurely. 212390075Sobrien * Perhaps the file was externally truncated. 212418334Speter */ 212596263Sobrien if (cnt == 0) { 212696263Sobrien msg = "Transfer finished due to " 212796263Sobrien "premature end of file."; 212890075Sobrien break; 212990075Sobrien } 213018334Speter } 213118334Speter ENDXFER; 213218334Speter reply(226, msg); 213390075Sobrien return (0); 213490075Sobrien } 213518334Speter 213618334Speteroldway: 213790075Sobrien if ((buf = malloc(blksize)) == NULL) { 213890075Sobrien ENDXFER; 213918334Speter reply(451, "Ran out of memory."); 214090075Sobrien return (-1); 214190075Sobrien } 214218334Speter 214390075Sobrien for (;;) { 214490075Sobrien int cnt, len; 214590075Sobrien char *bp; 214618334Speter 214790075Sobrien cnt = read(filefd, buf, blksize); 214890075Sobrien CHECKOOB(free(buf); return (-1)) 214990075Sobrien else if (cnt < 0) { 215018334Speter free(buf); 215190075Sobrien goto file_err; 215290075Sobrien } 215390075Sobrien if (cnt < 0) /* resume after OOB */ 215418334Speter continue; 215590075Sobrien if (cnt == 0) /* EOF */ 215690075Sobrien break; 215790075Sobrien for (len = cnt, bp = buf; len > 0;) { 215890075Sobrien cnt = write(netfd, bp, len); 215990075Sobrien CHECKOOB(free(buf); return (-1)) 216090075Sobrien else if (cnt < 0) { 216190075Sobrien free(buf); 216290075Sobrien goto data_err; 216390075Sobrien } 216490075Sobrien if (cnt <= 0) 216590075Sobrien continue; 216690075Sobrien len -= cnt; 216790075Sobrien bp += cnt; 216890075Sobrien byte_count += cnt; 216990075Sobrien } 217090075Sobrien } 217190075Sobrien ENDXFER; 217290075Sobrien free(buf); 217390075Sobrien reply(226, "Transfer complete."); 217490075Sobrien return (0); 217518334Speter default: 217690075Sobrien ENDXFER; 217790075Sobrien reply(550, "Unimplemented TYPE %d in send_data.", type); 217818334Speter return (-1); 217918334Speter } 218090075Sobrien 218118334Speterdata_err: 218290075Sobrien ENDXFER; 218390075Sobrien perror_reply(426, "Data connection"); 218418334Speter return (-1); 218590075Sobrien 218690075Sobrienfile_err: 218718334Speter ENDXFER; 218818334Speter perror_reply(551, "Error on input file"); 218990075Sobrien return (-1); 219090075Sobrien} 219190075Sobrien 219218334Speter/* 219390075Sobrien * Transfer data from peer to "outstr" using the appropriate encapulation of 219418334Speter * the data subject to Mode, Structure, and Type. 219518334Speter * 219690075Sobrien * N.B.: Form isn't handled. 219790075Sobrien */ 219890075Sobrienstatic int 219990075Sobrienreceive_data(FILE *instr, FILE *outstr) 220018334Speter{ 220190075Sobrien int c, cp; 220290075Sobrien int bare_lfs = 0; 220318334Speter 220418334Speter STARTXFER; 220590075Sobrien 220618334Speter switch (type) { 220790075Sobrien 220890075Sobrien case TYPE_I: 220990075Sobrien case TYPE_L: 221090075Sobrien for (;;) { 221118334Speter int cnt, len; 221290075Sobrien char *bp; 221318334Speter char buf[BUFSIZ]; 221418334Speter 221590075Sobrien cnt = read(fileno(instr), buf, sizeof(buf)); 221690075Sobrien CHECKOOB(return (-1)) 221718334Speter else if (cnt < 0) 221890075Sobrien goto data_err; 221990075Sobrien if (cnt < 0) /* resume after OOB */ 222090075Sobrien continue; 222190075Sobrien if (cnt == 0) /* EOF */ 222218334Speter break; 222390075Sobrien for (len = cnt, bp = buf; len > 0;) { 222490075Sobrien cnt = write(fileno(outstr), bp, len); 222590075Sobrien CHECKOOB(return (-1)) 222690075Sobrien else if (cnt < 0) 222718334Speter goto file_err; 222890075Sobrien if (cnt <= 0) 222990075Sobrien continue; 223090075Sobrien len -= cnt; 223190075Sobrien bp += cnt; 223218334Speter byte_count += cnt; 223390075Sobrien } 223490075Sobrien } 223590075Sobrien ENDXFER; 223618334Speter return (0); 223718334Speter 223890075Sobrien case TYPE_E: 223990075Sobrien ENDXFER; 224090075Sobrien reply(553, "TYPE E not implemented."); 224118334Speter return (-1); 224290075Sobrien 224318334Speter case TYPE_A: 224490075Sobrien cp = EOF; 224518334Speter for (;;) { 224618334Speter c = getc(instr); 224718334Speter CHECKOOB(return (-1)) 224818334Speter else if (c == EOF && ferror(instr)) 224952284Sobrien goto data_err; 225018334Speter if (c == EOF && ferror(instr)) { /* resume after OOB */ 225152284Sobrien clearerr(instr); 225252284Sobrien continue; 225352284Sobrien } 225452284Sobrien 225552284Sobrien if (cp == '\r') { 225652284Sobrien if (c != '\n') 225752284Sobrien FTPD_PUTC('\r', outstr, file_err); 225852284Sobrien } else 225918334Speter if (c == '\n') 226018334Speter bare_lfs++; 226118334Speter if (c == '\r') { 226218334Speter byte_count++; 226390075Sobrien cp = c; 226490075Sobrien continue; 226590075Sobrien } 226690075Sobrien 226718334Speter /* Check for EOF here in order not to lose last \r. */ 226818334Speter if (c == EOF) { 226918334Speter if (feof(instr)) /* EOF */ 227018334Speter break; 227118334Speter syslog(LOG_ERR, "Internal: impossible condition" 227218334Speter " on data stream after getc()"); 227318334Speter goto data_err; 227418334Speter } 227590075Sobrien 227618334Speter byte_count++; 227718334Speter FTPD_PUTC(c, outstr, file_err); 227818334Speter cp = c; 227918334Speter } 228018334Speter#ifdef notyet /* BSD stdio isn't ready for that */ 228118334Speter while (fflush(outstr) == EOF) { 228218334Speter CHECKOOB(return (-1)) 228318334Speter else 228418334Speter goto file_err; 228518334Speter clearerr(outstr); 228618334Speter } 228718334Speter ENDXFER; 228890075Sobrien#else 228918334Speter ENDXFER; 229018334Speter if (fflush(outstr) == EOF) 229118334Speter goto file_err; 229290075Sobrien#endif 229390075Sobrien if (bare_lfs) { 229490075Sobrien lreply(226, 229590075Sobrien "WARNING! %d bare linefeeds received in ASCII mode.", 229690075Sobrien bare_lfs); 229718334Speter (void)printf(" File may not have transferred correctly.\r\n"); 229818334Speter } 229918334Speter return (0); 230018334Speter default: 230118334Speter ENDXFER; 230218334Speter reply(550, "Unimplemented TYPE %d in receive_data.", type); 230318334Speter return (-1); 230418334Speter } 230518334Speter 230618334Speterdata_err: 230718334Speter ENDXFER; 230818334Speter perror_reply(426, "Data connection"); 230990075Sobrien return (-1); 231018334Speter 231118334Speterfile_err: 231218334Speter ENDXFER; 231390075Sobrien perror_reply(452, "Error writing to file"); 231490075Sobrien return (-1); 231590075Sobrien} 231690075Sobrien 231790075Sobrienvoid 231818334Speterstatfilecmd(char *filename) 231918334Speter{ 232018334Speter FILE *fin; 232118334Speter int atstart; 232218334Speter int c, code; 232318334Speter char line[LINE_MAX]; 232418334Speter struct stat st; 232518334Speter 232618334Speter code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; 232790075Sobrien (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); 232818334Speter fin = ftpd_popen(line, "r"); 232918334Speter lreply(code, "Status of %s:", filename); 233090075Sobrien atstart = 1; 233118334Speter while ((c = getc(fin)) != EOF) { 233218334Speter if (c == '\n') { 233318334Speter if (ferror(stdout)){ 233418334Speter perror_reply(421, "Control connection"); 233518334Speter (void) ftpd_pclose(fin); 233618334Speter dologout(1); 233718334Speter /* NOTREACHED */ 233818334Speter } 233918334Speter if (ferror(fin)) { 234018334Speter perror_reply(551, filename); 234118334Speter (void) ftpd_pclose(fin); 234218334Speter return; 234318334Speter } 234418334Speter (void) putc('\r', stdout); 234518334Speter } 234618334Speter /* 234718334Speter * RFC 959 says neutral text should be prepended before 234818334Speter * a leading 3-digit number followed by whitespace, but 234918334Speter * many ftp clients can be confused by any leading digits, 235018334Speter * as a matter of fact. 235118334Speter */ 235218334Speter if (atstart && isdigit(c)) 235318334Speter (void) putc(' ', stdout); 235418334Speter (void) putc(c, stdout); 235518334Speter atstart = (c == '\n'); 235618334Speter } 235718334Speter (void) ftpd_pclose(fin); 235850397Sobrien reply(code, "End of status."); 235950397Sobrien} 236050397Sobrien 236150397Sobrienvoid 236250397Sobrienstatcmd(void) 236350397Sobrien{ 236450397Sobrien union sockunion *su; 236518334Speter u_char *a, *p; 236618334Speter char hname[NI_MAXHOST]; 236718334Speter int ispassive; 236818334Speter 236918334Speter if (hostinfo) { 237018334Speter lreply(211, "%s FTP server status:", hostname); 237118334Speter printf(" %s\r\n", version); 237218334Speter } else 237352284Sobrien lreply(211, "FTP server status:"); 237452284Sobrien printf(" Connected to %s", remotehost); 237552284Sobrien if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 237652284Sobrien hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { 237752284Sobrien hname[sizeof(hname) - 1] = 0; 237818334Speter if (strcmp(hname, remotehost) != 0) 237918334Speter printf(" (%s)", hname); 238018334Speter } 238118334Speter printf("\r\n"); 238218334Speter if (logged_in) { 238318334Speter if (guest) 238418334Speter printf(" Logged in anonymously\r\n"); 238518334Speter else 238618334Speter printf(" Logged in as %s\r\n", pw->pw_name); 238718334Speter } else if (askpasswd) 238818334Speter printf(" Waiting for password\r\n"); 238918334Speter else 239018334Speter printf(" Waiting for user name\r\n"); 239118334Speter printf(" TYPE: %s", typenames[type]); 239218334Speter if (type == TYPE_A || type == TYPE_E) 239318334Speter printf(", FORM: %s", formnames[form]); 239490075Sobrien if (type == TYPE_L) 239518334Speter#if CHAR_BIT == 8 239618334Speter printf(" %d", CHAR_BIT); 239718334Speter#else 239818334Speter printf(" %d", bytesize); /* need definition! */ 239918334Speter#endif 240018334Speter printf("; STRUcture: %s; transfer MODE: %s\r\n", 240118334Speter strunames[stru], modenames[mode]); 240218334Speter if (data != -1) 240318334Speter printf(" Data connection open\r\n"); 240418334Speter else if (pdata != -1) { 240518334Speter ispassive = 1; 240618334Speter su = &pasv_addr; 240790075Sobrien goto printaddr; 240890075Sobrien } else if (usedefault == 0) { 240990075Sobrien ispassive = 0; 241090075Sobrien su = &data_dest; 241190075Sobrienprintaddr: 241218334Speter#define UC(b) (((int) b) & 0xff) 241318334Speter if (epsvall) { 241418334Speter printf(" EPSV only mode (EPSV ALL)\r\n"); 241518334Speter goto epsvonly; 241618334Speter } 241718334Speter 241818334Speter /* PORT/PASV */ 241918334Speter if (su->su_family == AF_INET) { 242018334Speter a = (u_char *) &su->su_sin.sin_addr; 242118334Speter p = (u_char *) &su->su_sin.sin_port; 242218334Speter printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", 242318334Speter ispassive ? "PASV" : "PORT", 242418334Speter UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 242518334Speter UC(p[0]), UC(p[1])); 242618334Speter } 242718334Speter 242818334Speter /* LPRT/LPSV */ 242918334Speter { 243018334Speter int alen, af, i; 243118334Speter 243218334Speter switch (su->su_family) { 243318334Speter case AF_INET: 243418334Speter a = (u_char *) &su->su_sin.sin_addr; 243518334Speter p = (u_char *) &su->su_sin.sin_port; 243618334Speter alen = sizeof(su->su_sin.sin_addr); 243718334Speter af = 4; 243818334Speter break; 243918334Speter case AF_INET6: 244018334Speter a = (u_char *) &su->su_sin6.sin6_addr; 244118334Speter p = (u_char *) &su->su_sin6.sin6_port; 244218334Speter alen = sizeof(su->su_sin6.sin6_addr); 244318334Speter af = 6; 244418334Speter break; 244518334Speter default: 244618334Speter af = 0; 244718334Speter break; 244818334Speter } 244918334Speter if (af) { 245018334Speter printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", 245118334Speter af, alen); 245218334Speter for (i = 0; i < alen; i++) 245318334Speter printf("%d,", UC(a[i])); 245418334Speter printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); 245518334Speter } 245618334Speter } 245718334Speter 245818334Speterepsvonly:; 245918334Speter /* EPRT/EPSV */ 246018334Speter { 246118334Speter int af; 246218334Speter 246390075Sobrien switch (su->su_family) { 246490075Sobrien case AF_INET: 246590075Sobrien af = 1; 246618334Speter break; 246718334Speter case AF_INET6: 246818334Speter af = 2; 2469104752Skan break; 2470104752Skan default: 2471104752Skan af = 0; 2472104752Skan break; 2473104752Skan } 2474104752Skan if (af) { 2475104752Skan union sockunion tmp; 2476104752Skan 2477104752Skan tmp = *su; 2478104752Skan if (tmp.su_family == AF_INET6) 2479104752Skan tmp.su_sin6.sin6_scope_id = 0; 2480104752Skan if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, 2481 hname, sizeof(hname) - 1, NULL, 0, 2482 NI_NUMERICHOST)) { 2483 hname[sizeof(hname) - 1] = 0; 2484 printf(" %s |%d|%s|%d|\r\n", 2485 ispassive ? "EPSV" : "EPRT", 2486 af, hname, htons(tmp.su_port)); 2487 } 2488 } 2489 } 2490#undef UC 2491 } else 2492 printf(" No data connection\r\n"); 2493 reply(211, "End of status."); 2494} 2495 2496void 2497fatalerror(char *s) 2498{ 2499 2500 reply(451, "Error in server: %s", s); 2501 reply(221, "Closing connection due to server error."); 2502 dologout(0); 2503 /* NOTREACHED */ 2504} 2505 2506void 2507reply(int n, const char *fmt, ...) 2508{ 2509 va_list ap; 2510 2511 (void)printf("%d ", n); 2512 va_start(ap, fmt); 2513 (void)vprintf(fmt, ap); 2514 va_end(ap); 2515 (void)printf("\r\n"); 2516 (void)fflush(stdout); 2517 if (ftpdebug) { 2518 syslog(LOG_DEBUG, "<--- %d ", n); 2519 va_start(ap, fmt); 2520 vsyslog(LOG_DEBUG, fmt, ap); 2521 va_end(ap); 2522 } 2523} 2524 2525void 2526lreply(int n, const char *fmt, ...) 2527{ 2528 va_list ap; 2529 2530 (void)printf("%d- ", n); 2531 va_start(ap, fmt); 2532 (void)vprintf(fmt, ap); 2533 va_end(ap); 2534 (void)printf("\r\n"); 2535 (void)fflush(stdout); 2536 if (ftpdebug) { 2537 syslog(LOG_DEBUG, "<--- %d- ", n); 2538 va_start(ap, fmt); 2539 vsyslog(LOG_DEBUG, fmt, ap); 2540 va_end(ap); 2541 } 2542} 2543 2544static void 2545ack(char *s) 2546{ 2547 2548 reply(250, "%s command successful.", s); 2549} 2550 2551void 2552nack(char *s) 2553{ 2554 2555 reply(502, "%s command not implemented.", s); 2556} 2557 2558/* ARGSUSED */ 2559void 2560yyerror(char *s) 2561{ 2562 char *cp; 2563 2564 if ((cp = strchr(cbuf,'\n'))) 2565 *cp = '\0'; 2566 reply(500, "%s: command not understood.", cbuf); 2567} 2568 2569void 2570delete(char *name) 2571{ 2572 struct stat st; 2573 2574 LOGCMD("delete", name); 2575 if (lstat(name, &st) < 0) { 2576 perror_reply(550, name); 2577 return; 2578 } 2579 if (S_ISDIR(st.st_mode)) { 2580 if (rmdir(name) < 0) { 2581 perror_reply(550, name); 2582 return; 2583 } 2584 goto done; 2585 } 2586 if (guest && noguestmod) { 2587 reply(550, "Operation not permitted."); 2588 return; 2589 } 2590 if (unlink(name) < 0) { 2591 perror_reply(550, name); 2592 return; 2593 } 2594done: 2595 ack("DELE"); 2596} 2597 2598void 2599cwd(char *path) 2600{ 2601 2602 if (chdir(path) < 0) 2603 perror_reply(550, path); 2604 else 2605 ack("CWD"); 2606} 2607 2608void 2609makedir(char *name) 2610{ 2611 char *s; 2612 2613 LOGCMD("mkdir", name); 2614 if (guest && noguestmkd) 2615 reply(550, "Operation not permitted."); 2616 else if (mkdir(name, 0777) < 0) 2617 perror_reply(550, name); 2618 else { 2619 if ((s = doublequote(name)) == NULL) 2620 fatalerror("Ran out of memory."); 2621 reply(257, "\"%s\" directory created.", s); 2622 free(s); 2623 } 2624} 2625 2626void 2627removedir(char *name) 2628{ 2629 2630 LOGCMD("rmdir", name); 2631 if (rmdir(name) < 0) 2632 perror_reply(550, name); 2633 else 2634 ack("RMD"); 2635} 2636 2637void 2638pwd(void) 2639{ 2640 char *s, path[MAXPATHLEN + 1]; 2641 2642 if (getcwd(path, sizeof(path)) == NULL) 2643 perror_reply(550, "Get current directory"); 2644 else { 2645 if ((s = doublequote(path)) == NULL) 2646 fatalerror("Ran out of memory."); 2647 reply(257, "\"%s\" is current directory.", s); 2648 free(s); 2649 } 2650} 2651 2652char * 2653renamefrom(char *name) 2654{ 2655 struct stat st; 2656 2657 if (guest && noguestmod) { 2658 reply(550, "Operation not permitted."); 2659 return (NULL); 2660 } 2661 if (lstat(name, &st) < 0) { 2662 perror_reply(550, name); 2663 return (NULL); 2664 } 2665 reply(350, "File exists, ready for destination name."); 2666 return (name); 2667} 2668 2669void 2670renamecmd(char *from, char *to) 2671{ 2672 struct stat st; 2673 2674 LOGCMD2("rename", from, to); 2675 2676 if (guest && (stat(to, &st) == 0)) { 2677 reply(550, "%s: permission denied.", to); 2678 return; 2679 } 2680 2681 if (rename(from, to) < 0) 2682 perror_reply(550, "rename"); 2683 else 2684 ack("RNTO"); 2685} 2686 2687static void 2688dolog(struct sockaddr *who) 2689{ 2690 char who_name[NI_MAXHOST]; 2691 2692 realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); 2693 remotehost[sizeof(remotehost) - 1] = 0; 2694 if (getnameinfo(who, who->sa_len, 2695 who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) 2696 *who_name = 0; 2697 who_name[sizeof(who_name) - 1] = 0; 2698 2699#ifdef SETPROCTITLE 2700#ifdef VIRTUAL_HOSTING 2701 if (thishost != firsthost) 2702 snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", 2703 remotehost, hostname); 2704 else 2705#endif 2706 snprintf(proctitle, sizeof(proctitle), "%s: connected", 2707 remotehost); 2708 setproctitle("%s", proctitle); 2709#endif /* SETPROCTITLE */ 2710 2711 if (logging) { 2712#ifdef VIRTUAL_HOSTING 2713 if (thishost != firsthost) 2714 syslog(LOG_INFO, "connection from %s (%s) to %s", 2715 remotehost, who_name, hostname); 2716 else 2717#endif 2718 syslog(LOG_INFO, "connection from %s (%s)", 2719 remotehost, who_name); 2720 } 2721} 2722 2723/* 2724 * Record logout in wtmp file 2725 * and exit with supplied status. 2726 */ 2727void 2728dologout(int status) 2729{ 2730 2731 if (logged_in && dowtmp) { 2732 (void) seteuid(0); 2733 ftpd_logwtmp(ttyline, "", NULL); 2734 } 2735 /* beware of flushing buffers after a SIGPIPE */ 2736 _exit(status); 2737} 2738 2739static void 2740sigurg(int signo) 2741{ 2742 2743 recvurg = 1; 2744} 2745 2746static void 2747maskurg(int flag) 2748{ 2749 int oerrno; 2750 sigset_t sset; 2751 2752 if (!transflag) { 2753 syslog(LOG_ERR, "Internal: maskurg() while no transfer"); 2754 return; 2755 } 2756 oerrno = errno; 2757 sigemptyset(&sset); 2758 sigaddset(&sset, SIGURG); 2759 sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); 2760 errno = oerrno; 2761} 2762 2763static void 2764flagxfer(int flag) 2765{ 2766 2767 maskurg(!flag); 2768 if (flag) { 2769 recvurg = 0; 2770 transflag = 1; 2771 } else 2772 transflag = 0; 2773} 2774 2775/* 2776 * Returns 0 if OK to resume or -1 if abort requested. 2777 */ 2778static int 2779myoob(void) 2780{ 2781 char *cp; 2782 2783 if (!transflag) { 2784 syslog(LOG_ERR, "Internal: myoob() while no transfer"); 2785 return (0); 2786 } 2787 cp = tmpline; 2788 if (getline(cp, 7, stdin) == NULL) { 2789 reply(221, "You could at least say goodbye."); 2790 dologout(0); 2791 } 2792 upper(cp); 2793 if (strcmp(cp, "ABOR\r\n") == 0) { 2794 tmpline[0] = '\0'; 2795 reply(426, "Transfer aborted. Data connection closed."); 2796 reply(226, "Abort successful."); 2797 return (-1); 2798 } 2799 if (strcmp(cp, "STAT\r\n") == 0) { 2800 tmpline[0] = '\0'; 2801 if (file_size != -1) 2802 reply(213, "Status: %jd of %jd bytes transferred.", 2803 (intmax_t)byte_count, (intmax_t)file_size); 2804 else 2805 reply(213, "Status: %jd bytes transferred.", 2806 (intmax_t)byte_count); 2807 } 2808 return (0); 2809} 2810 2811/* 2812 * Note: a response of 425 is not mentioned as a possible response to 2813 * the PASV command in RFC959. However, it has been blessed as 2814 * a legitimate response by Jon Postel in a telephone conversation 2815 * with Rick Adams on 25 Jan 89. 2816 */ 2817void 2818passive(void) 2819{ 2820 socklen_t len; 2821 int on; 2822 char *p, *a; 2823 2824 if (pdata >= 0) /* close old port if one set */ 2825 close(pdata); 2826 2827 pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 2828 if (pdata < 0) { 2829 perror_reply(425, "Can't open passive connection"); 2830 return; 2831 } 2832 on = 1; 2833 if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2834 syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 2835 2836 (void) seteuid(0); 2837 2838#ifdef IP_PORTRANGE 2839 if (ctrl_addr.su_family == AF_INET) { 2840 on = restricted_data_ports ? IP_PORTRANGE_HIGH 2841 : IP_PORTRANGE_DEFAULT; 2842 2843 if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2844 &on, sizeof(on)) < 0) 2845 goto pasv_error; 2846 } 2847#endif 2848#ifdef IPV6_PORTRANGE 2849 if (ctrl_addr.su_family == AF_INET6) { 2850 on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2851 : IPV6_PORTRANGE_DEFAULT; 2852 2853 if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2854 &on, sizeof(on)) < 0) 2855 goto pasv_error; 2856 } 2857#endif 2858 2859 pasv_addr = ctrl_addr; 2860 pasv_addr.su_port = 0; 2861 if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) 2862 goto pasv_error; 2863 2864 (void) seteuid(pw->pw_uid); 2865 2866 len = sizeof(pasv_addr); 2867 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 2868 goto pasv_error; 2869 if (listen(pdata, 1) < 0) 2870 goto pasv_error; 2871 if (pasv_addr.su_family == AF_INET) 2872 a = (char *) &pasv_addr.su_sin.sin_addr; 2873 else if (pasv_addr.su_family == AF_INET6 && 2874 IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) 2875 a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 2876 else 2877 goto pasv_error; 2878 2879 p = (char *) &pasv_addr.su_port; 2880 2881#define UC(b) (((int) b) & 0xff) 2882 2883 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 2884 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 2885 return; 2886 2887pasv_error: 2888 (void) seteuid(pw->pw_uid); 2889 (void) close(pdata); 2890 pdata = -1; 2891 perror_reply(425, "Can't open passive connection"); 2892 return; 2893} 2894 2895/* 2896 * Long Passive defined in RFC 1639. 2897 * 228 Entering Long Passive Mode 2898 * (af, hal, h1, h2, h3,..., pal, p1, p2...) 2899 */ 2900 2901void 2902long_passive(char *cmd, int pf) 2903{ 2904 socklen_t len; 2905 int on; 2906 char *p, *a; 2907 2908 if (pdata >= 0) /* close old port if one set */ 2909 close(pdata); 2910 2911 if (pf != PF_UNSPEC) { 2912 if (ctrl_addr.su_family != pf) { 2913 switch (ctrl_addr.su_family) { 2914 case AF_INET: 2915 pf = 1; 2916 break; 2917 case AF_INET6: 2918 pf = 2; 2919 break; 2920 default: 2921 pf = 0; 2922 break; 2923 } 2924 /* 2925 * XXX 2926 * only EPRT/EPSV ready clients will understand this 2927 */ 2928 if (strcmp(cmd, "EPSV") == 0 && pf) { 2929 reply(522, "Network protocol mismatch, " 2930 "use (%d)", pf); 2931 } else 2932 reply(501, "Network protocol mismatch."); /*XXX*/ 2933 2934 return; 2935 } 2936 } 2937 2938 pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); 2939 if (pdata < 0) { 2940 perror_reply(425, "Can't open passive connection"); 2941 return; 2942 } 2943 on = 1; 2944 if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2945 syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); 2946 2947 (void) seteuid(0); 2948 2949 pasv_addr = ctrl_addr; 2950 pasv_addr.su_port = 0; 2951 len = pasv_addr.su_len; 2952 2953#ifdef IP_PORTRANGE 2954 if (ctrl_addr.su_family == AF_INET) { 2955 on = restricted_data_ports ? IP_PORTRANGE_HIGH 2956 : IP_PORTRANGE_DEFAULT; 2957 2958 if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, 2959 &on, sizeof(on)) < 0) 2960 goto pasv_error; 2961 } 2962#endif 2963#ifdef IPV6_PORTRANGE 2964 if (ctrl_addr.su_family == AF_INET6) { 2965 on = restricted_data_ports ? IPV6_PORTRANGE_HIGH 2966 : IPV6_PORTRANGE_DEFAULT; 2967 2968 if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, 2969 &on, sizeof(on)) < 0) 2970 goto pasv_error; 2971 } 2972#endif 2973 2974 if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) 2975 goto pasv_error; 2976 2977 (void) seteuid(pw->pw_uid); 2978 2979 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 2980 goto pasv_error; 2981 if (listen(pdata, 1) < 0) 2982 goto pasv_error; 2983 2984#define UC(b) (((int) b) & 0xff) 2985 2986 if (strcmp(cmd, "LPSV") == 0) { 2987 p = (char *)&pasv_addr.su_port; 2988 switch (pasv_addr.su_family) { 2989 case AF_INET: 2990 a = (char *) &pasv_addr.su_sin.sin_addr; 2991 v4_reply: 2992 reply(228, 2993"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 2994 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 2995 2, UC(p[0]), UC(p[1])); 2996 return; 2997 case AF_INET6: 2998 if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { 2999 a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; 3000 goto v4_reply; 3001 } 3002 a = (char *) &pasv_addr.su_sin6.sin6_addr; 3003 reply(228, 3004"Entering Long Passive Mode " 3005"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 3006 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 3007 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), 3008 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), 3009 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 3010 2, UC(p[0]), UC(p[1])); 3011 return; 3012 } 3013 } else if (strcmp(cmd, "EPSV") == 0) { 3014 switch (pasv_addr.su_family) { 3015 case AF_INET: 3016 case AF_INET6: 3017 reply(229, "Entering Extended Passive Mode (|||%d|)", 3018 ntohs(pasv_addr.su_port)); 3019 return; 3020 } 3021 } else { 3022 /* more proper error code? */ 3023 } 3024 3025pasv_error: 3026 (void) seteuid(pw->pw_uid); 3027 (void) close(pdata); 3028 pdata = -1; 3029 perror_reply(425, "Can't open passive connection"); 3030 return; 3031} 3032 3033/* 3034 * Generate unique name for file with basename "local" 3035 * and open the file in order to avoid possible races. 3036 * Try "local" first, then "local.1", "local.2" etc, up to "local.99". 3037 * Return descriptor to the file, set "name" to its name. 3038 * 3039 * Generates failure reply on error. 3040 */ 3041static int 3042guniquefd(char *local, char **name) 3043{ 3044 static char new[MAXPATHLEN]; 3045 struct stat st; 3046 char *cp; 3047 int count; 3048 int fd; 3049 3050 cp = strrchr(local, '/'); 3051 if (cp) 3052 *cp = '\0'; 3053 if (stat(cp ? local : ".", &st) < 0) { 3054 perror_reply(553, cp ? local : "."); 3055 return (-1); 3056 } 3057 if (cp) { 3058 /* 3059 * Let not overwrite dirname with counter suffix. 3060 * -4 is for /nn\0 3061 * In this extreme case dot won't be put in front of suffix. 3062 */ 3063 if (strlen(local) > sizeof(new) - 4) { 3064 reply(553, "Pathname too long."); 3065 return (-1); 3066 } 3067 *cp = '/'; 3068 } 3069 /* -4 is for the .nn<null> we put on the end below */ 3070 (void) snprintf(new, sizeof(new) - 4, "%s", local); 3071 cp = new + strlen(new); 3072 /* 3073 * Don't generate dotfile unless requested explicitly. 3074 * This covers the case when basename gets truncated off 3075 * by buffer size. 3076 */ 3077 if (cp > new && cp[-1] != '/') 3078 *cp++ = '.'; 3079 for (count = 0; count < 100; count++) { 3080 /* At count 0 try unmodified name */ 3081 if (count) 3082 (void)sprintf(cp, "%d", count); 3083 if ((fd = open(count ? new : local, 3084 O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { 3085 *name = count ? new : local; 3086 return (fd); 3087 } 3088 if (errno != EEXIST) { 3089 perror_reply(553, count ? new : local); 3090 return (-1); 3091 } 3092 } 3093 reply(452, "Unique file name cannot be created."); 3094 return (-1); 3095} 3096 3097/* 3098 * Format and send reply containing system error number. 3099 */ 3100void 3101perror_reply(int code, char *string) 3102{ 3103 3104 reply(code, "%s: %s.", string, strerror(errno)); 3105} 3106 3107static char *onefile[] = { 3108 "", 3109 0 3110}; 3111 3112void 3113send_file_list(char *whichf) 3114{ 3115 struct stat st; 3116 DIR *dirp = NULL; 3117 struct dirent *dir; 3118 FILE *dout = NULL; 3119 char **dirlist, *dirname; 3120 int simple = 0; 3121 int freeglob = 0; 3122 glob_t gl; 3123 3124 if (strpbrk(whichf, "~{[*?") != NULL) { 3125 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 3126 3127 memset(&gl, 0, sizeof(gl)); 3128 gl.gl_matchc = MAXGLOBARGS; 3129 flags |= GLOB_LIMIT; 3130 freeglob = 1; 3131 if (glob(whichf, flags, 0, &gl)) { 3132 reply(550, "No matching files found."); 3133 goto out; 3134 } else if (gl.gl_pathc == 0) { 3135 errno = ENOENT; 3136 perror_reply(550, whichf); 3137 goto out; 3138 } 3139 dirlist = gl.gl_pathv; 3140 } else { 3141 onefile[0] = whichf; 3142 dirlist = onefile; 3143 simple = 1; 3144 } 3145 3146 while ((dirname = *dirlist++)) { 3147 if (stat(dirname, &st) < 0) { 3148 /* 3149 * If user typed "ls -l", etc, and the client 3150 * used NLST, do what the user meant. 3151 */ 3152 if (dirname[0] == '-' && *dirlist == NULL && 3153 dout == NULL) 3154 retrieve(_PATH_LS " %s", dirname); 3155 else 3156 perror_reply(550, whichf); 3157 goto out; 3158 } 3159 3160 if (S_ISREG(st.st_mode)) { 3161 if (dout == NULL) { 3162 dout = dataconn("file list", -1, "w"); 3163 if (dout == NULL) 3164 goto out; 3165 STARTXFER; 3166 } 3167 START_UNSAFE; 3168 fprintf(dout, "%s%s\n", dirname, 3169 type == TYPE_A ? "\r" : ""); 3170 END_UNSAFE; 3171 if (ferror(dout)) 3172 goto data_err; 3173 byte_count += strlen(dirname) + 3174 (type == TYPE_A ? 2 : 1); 3175 CHECKOOB(goto abrt); 3176 continue; 3177 } else if (!S_ISDIR(st.st_mode)) 3178 continue; 3179 3180 if ((dirp = opendir(dirname)) == NULL) 3181 continue; 3182 3183 while ((dir = readdir(dirp)) != NULL) { 3184 char nbuf[MAXPATHLEN]; 3185 3186 CHECKOOB(goto abrt); 3187 3188 if (dir->d_name[0] == '.' && dir->d_namlen == 1) 3189 continue; 3190 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 3191 dir->d_namlen == 2) 3192 continue; 3193 3194 snprintf(nbuf, sizeof(nbuf), 3195 "%s/%s", dirname, dir->d_name); 3196 3197 /* 3198 * We have to do a stat to insure it's 3199 * not a directory or special file. 3200 */ 3201 if (simple || (stat(nbuf, &st) == 0 && 3202 S_ISREG(st.st_mode))) { 3203 if (dout == NULL) { 3204 dout = dataconn("file list", -1, "w"); 3205 if (dout == NULL) 3206 goto out; 3207 STARTXFER; 3208 } 3209 START_UNSAFE; 3210 if (nbuf[0] == '.' && nbuf[1] == '/') 3211 fprintf(dout, "%s%s\n", &nbuf[2], 3212 type == TYPE_A ? "\r" : ""); 3213 else 3214 fprintf(dout, "%s%s\n", nbuf, 3215 type == TYPE_A ? "\r" : ""); 3216 END_UNSAFE; 3217 if (ferror(dout)) 3218 goto data_err; 3219 byte_count += strlen(nbuf) + 3220 (type == TYPE_A ? 2 : 1); 3221 CHECKOOB(goto abrt); 3222 } 3223 } 3224 (void) closedir(dirp); 3225 dirp = NULL; 3226 } 3227 3228 if (dout == NULL) 3229 reply(550, "No files found."); 3230 else if (ferror(dout)) 3231data_err: perror_reply(550, "Data connection"); 3232 else 3233 reply(226, "Transfer complete."); 3234out: 3235 if (dout) { 3236 ENDXFER; 3237abrt: 3238 (void) fclose(dout); 3239 data = -1; 3240 pdata = -1; 3241 } 3242 if (dirp) 3243 (void) closedir(dirp); 3244 if (freeglob) { 3245 freeglob = 0; 3246 globfree(&gl); 3247 } 3248} 3249 3250void 3251reapchild(int signo) 3252{ 3253 while (waitpid(-1, NULL, WNOHANG) > 0); 3254} 3255 3256#ifdef OLD_SETPROCTITLE 3257/* 3258 * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 3259 * Warning, since this is usually started from inetd.conf, it often doesn't 3260 * have much of an environment or arglist to overwrite. 3261 */ 3262void 3263setproctitle(const char *fmt, ...) 3264{ 3265 int i; 3266 va_list ap; 3267 char *p, *bp, ch; 3268 char buf[LINE_MAX]; 3269 3270 va_start(ap, fmt); 3271 (void)vsnprintf(buf, sizeof(buf), fmt, ap); 3272 3273 /* make ps print our process name */ 3274 p = Argv[0]; 3275 *p++ = '-'; 3276 3277 i = strlen(buf); 3278 if (i > LastArgv - p - 2) { 3279 i = LastArgv - p - 2; 3280 buf[i] = '\0'; 3281 } 3282 bp = buf; 3283 while (ch = *bp++) 3284 if (ch != '\n' && ch != '\r') 3285 *p++ = ch; 3286 while (p < LastArgv) 3287 *p++ = ' '; 3288} 3289#endif /* OLD_SETPROCTITLE */ 3290 3291static void 3292appendf(char **strp, char *fmt, ...) 3293{ 3294 va_list ap; 3295 char *ostr, *p; 3296 3297 va_start(ap, fmt); 3298 vasprintf(&p, fmt, ap); 3299 va_end(ap); 3300 if (p == NULL) 3301 fatalerror("Ran out of memory."); 3302 if (*strp == NULL) 3303 *strp = p; 3304 else { 3305 ostr = *strp; 3306 asprintf(strp, "%s%s", ostr, p); 3307 if (*strp == NULL) 3308 fatalerror("Ran out of memory."); 3309 free(ostr); 3310 } 3311} 3312 3313static void 3314logcmd(char *cmd, char *file1, char *file2, off_t cnt) 3315{ 3316 char *msg = NULL; 3317 char wd[MAXPATHLEN + 1]; 3318 3319 if (logging <= 1) 3320 return; 3321 3322 if (getcwd(wd, sizeof(wd) - 1) == NULL) 3323 strcpy(wd, strerror(errno)); 3324 3325 appendf(&msg, "%s", cmd); 3326 if (file1) 3327 appendf(&msg, " %s", file1); 3328 if (file2) 3329 appendf(&msg, " %s", file2); 3330 if (cnt >= 0) 3331 appendf(&msg, " = %jd bytes", (intmax_t)cnt); 3332 appendf(&msg, " (wd: %s", wd); 3333 if (guest || dochroot) 3334 appendf(&msg, "; chrooted"); 3335 appendf(&msg, ")"); 3336 syslog(LOG_INFO, "%s", msg); 3337 free(msg); 3338} 3339 3340static void 3341logxfer(char *name, off_t size, time_t start) 3342{ 3343 char buf[MAXPATHLEN + 1024]; 3344 char path[MAXPATHLEN + 1]; 3345 time_t now; 3346 3347 if (statfd >= 0) { 3348 time(&now); 3349 if (realpath(name, path) == NULL) { 3350 syslog(LOG_NOTICE, "realpath failed on %s: %m", path); 3351 return; 3352 } 3353 snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", 3354 ctime(&now)+4, ident, remotehost, 3355 path, (intmax_t)size, 3356 (long)(now - start + (now == start))); 3357 write(statfd, buf, strlen(buf)); 3358 } 3359} 3360 3361static char * 3362doublequote(char *s) 3363{ 3364 int n; 3365 char *p, *s2; 3366 3367 for (p = s, n = 0; *p; p++) 3368 if (*p == '"') 3369 n++; 3370 3371 if ((s2 = malloc(p - s + n + 1)) == NULL) 3372 return (NULL); 3373 3374 for (p = s2; *s; s++, p++) { 3375 if ((*p = *s) == '"') 3376 *(++p) = '"'; 3377 } 3378 *p = '\0'; 3379 3380 return (s2); 3381} 3382 3383/* setup server socket for specified address family */ 3384/* if af is PF_UNSPEC more than one socket may be returned */ 3385/* the returned list is dynamically allocated, so caller needs to free it */ 3386static int * 3387socksetup(int af, char *bindname, const char *bindport) 3388{ 3389 struct addrinfo hints, *res, *r; 3390 int error, maxs, *s, *socks; 3391 const int on = 1; 3392 3393 memset(&hints, 0, sizeof(hints)); 3394 hints.ai_flags = AI_PASSIVE; 3395 hints.ai_family = af; 3396 hints.ai_socktype = SOCK_STREAM; 3397 error = getaddrinfo(bindname, bindport, &hints, &res); 3398 if (error) { 3399 syslog(LOG_ERR, "%s", gai_strerror(error)); 3400 if (error == EAI_SYSTEM) 3401 syslog(LOG_ERR, "%s", strerror(errno)); 3402 return NULL; 3403 } 3404 3405 /* Count max number of sockets we may open */ 3406 for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 3407 ; 3408 socks = malloc((maxs + 1) * sizeof(int)); 3409 if (!socks) { 3410 freeaddrinfo(res); 3411 syslog(LOG_ERR, "couldn't allocate memory for sockets"); 3412 return NULL; 3413 } 3414 3415 *socks = 0; /* num of sockets counter at start of array */ 3416 s = socks + 1; 3417 for (r = res; r; r = r->ai_next) { 3418 *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 3419 if (*s < 0) { 3420 syslog(LOG_DEBUG, "control socket: %m"); 3421 continue; 3422 } 3423 if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, 3424 &on, sizeof(on)) < 0) 3425 syslog(LOG_WARNING, 3426 "control setsockopt (SO_REUSEADDR): %m"); 3427 if (r->ai_family == AF_INET6) { 3428 if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 3429 &on, sizeof(on)) < 0) 3430 syslog(LOG_WARNING, 3431 "control setsockopt (IPV6_V6ONLY): %m"); 3432 } 3433 if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 3434 syslog(LOG_DEBUG, "control bind: %m"); 3435 close(*s); 3436 continue; 3437 } 3438 (*socks)++; 3439 s++; 3440 } 3441 3442 if (res) 3443 freeaddrinfo(res); 3444 3445 if (*socks == 0) { 3446 syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); 3447 free(socks); 3448 return NULL; 3449 } 3450 return(socks); 3451} 3452