Merge rev 1.2 (OPIE, login user capabilities database, PAM) support
into 'nbsd_20040809'.
This commit is contained in:
parent
07539dd5d7
commit
5bf06d869f
@ -1,7 +1,7 @@
|
||||
/* $NetBSD: ftpd.c,v 1.150 2003/01/22 04:46:08 lukem Exp $ */
|
||||
/* $NetBSD: ftpd.c,v 1.158 2004-08-09 12:56:47 lukem Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997-2001 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
@ -48,11 +48,7 @@
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
@ -109,7 +105,7 @@ __COPYRIGHT(
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
|
||||
#else
|
||||
__RCSID("$NetBSD: ftpd.c,v 1.150 2003/01/22 04:46:08 lukem Exp $");
|
||||
__RCSID("$NetBSD: ftpd.c,v 1.158 2004-08-09 12:56:47 lukem Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
__FBSDID("$FreeBSD$");
|
||||
@ -145,7 +141,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -169,14 +164,19 @@ __FBSDID("$FreeBSD$");
|
||||
#include <com_err.h>
|
||||
#include <krb5/krb5.h>
|
||||
#endif
|
||||
#ifdef LOGIN_CAP
|
||||
#include <login_cap.h>
|
||||
#endif
|
||||
|
||||
#define GLOBAL
|
||||
#include "extern.h"
|
||||
#include "pathnames.h"
|
||||
#include "version.h"
|
||||
|
||||
volatile sig_atomic_t transflag;
|
||||
volatile sig_atomic_t urgflag;
|
||||
|
||||
int data;
|
||||
jmp_buf urgcatch;
|
||||
int sflag;
|
||||
int stru; /* avoid C keyword */
|
||||
int mode;
|
||||
@ -184,7 +184,8 @@ int dataport; /* use specific data port */
|
||||
int dopidfile; /* maintain pid file */
|
||||
int doutmp; /* update utmp file */
|
||||
int dowtmp; /* update wtmp file */
|
||||
int doxferlog; /* syslog wu-ftpd style xferlog entries */
|
||||
int doxferlog; /* syslog/write wu-ftpd style xferlog entries */
|
||||
int xferlogfd; /* fd to write wu-ftpd xferlog entries to */
|
||||
int dropprivs; /* if privileges should or have been dropped */
|
||||
int mapped; /* IPv4 connection on AF_INET6 socket */
|
||||
off_t file_size;
|
||||
@ -200,6 +201,9 @@ static struct utmpx utmpx; /* for utmpx */
|
||||
static const char *anondir = NULL;
|
||||
static const char *confdir = _DEFAULT_CONFDIR;
|
||||
|
||||
static char *curname; /* current USER name */
|
||||
static size_t curname_len; /* length of curname (include NUL) */
|
||||
|
||||
#if defined(KERBEROS) || defined(KERBEROS5)
|
||||
int has_ccache = 0;
|
||||
int notickets = 1;
|
||||
@ -223,6 +227,7 @@ int swaitint = SWAITINT;
|
||||
|
||||
enum send_status {
|
||||
SS_SUCCESS,
|
||||
SS_ABORTED, /* transfer aborted */
|
||||
SS_NO_TRANSFER, /* no transfer made yet */
|
||||
SS_FILE_ERROR, /* file read error */
|
||||
SS_DATA_ERROR /* data send error */
|
||||
@ -253,7 +258,10 @@ static char *gunique(const char *);
|
||||
static void login_utmp(const char *, const char *, const char *);
|
||||
static void logremotehost(struct sockinet *);
|
||||
static void lostconn(int);
|
||||
static void myoob(int);
|
||||
static void toolong(int);
|
||||
static void sigquit(int);
|
||||
static void sigurg(int);
|
||||
static int handleoobcmd(void);
|
||||
static int receive_data(FILE *, FILE *);
|
||||
static int send_data(FILE *, FILE *, const struct stat *, int);
|
||||
static struct passwd *sgetpwnam(const char *);
|
||||
@ -285,7 +293,9 @@ main(int argc, char *argv[])
|
||||
krb5_error_code kerror;
|
||||
#endif
|
||||
char *p;
|
||||
const char *xferlogname = NULL;
|
||||
long l;
|
||||
struct sigaction sa;
|
||||
|
||||
connections = 1;
|
||||
debug = 0;
|
||||
@ -297,6 +307,7 @@ main(int argc, char *argv[])
|
||||
doutmp = 0; /* default: Do NOT log to utmp */
|
||||
dowtmp = 1; /* default: DO log to wtmp */
|
||||
doxferlog = 0; /* default: Do NOT syslog xferlog */
|
||||
xferlogfd = -1; /* default: Do NOT write xferlog file */
|
||||
dropprivs = 0;
|
||||
mapped = 0;
|
||||
usedefault = 1;
|
||||
@ -313,7 +324,7 @@ main(int argc, char *argv[])
|
||||
*/
|
||||
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
|
||||
|
||||
while ((ch = getopt(argc, argv, "a:c:C:de:h:HlP:qQrst:T:uUvV:wWX"))
|
||||
while ((ch = getopt(argc, argv, "a:c:C:de:h:HlL:P:qQrst:T:uUvV:wWX"))
|
||||
!= -1) {
|
||||
switch (ch) {
|
||||
case 'a':
|
||||
@ -352,6 +363,10 @@ main(int argc, char *argv[])
|
||||
logging++; /* > 1 == extra logging */
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
xferlogname = optarg;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
errno = 0;
|
||||
p = NULL;
|
||||
@ -413,7 +428,7 @@ main(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
doxferlog = 1;
|
||||
doxferlog |= 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -426,6 +441,23 @@ main(int argc, char *argv[])
|
||||
if (EMPTYSTR(confdir))
|
||||
confdir = _DEFAULT_CONFDIR;
|
||||
|
||||
errno = 0;
|
||||
l = sysconf(_SC_LOGIN_NAME_MAX);
|
||||
if (l == -1 && errno != 0) {
|
||||
syslog(LOG_ERR, "sysconf _SC_LOGIN_NAME_MAX: %m");
|
||||
exit(1);
|
||||
} else if (l <= 0) {
|
||||
syslog(LOG_WARNING, "using conservative LOGIN_NAME_MAX value");
|
||||
curname_len = _POSIX_LOGIN_NAME_MAX;
|
||||
} else
|
||||
curname_len = (size_t)l;
|
||||
curname = malloc(curname_len);
|
||||
if (curname == NULL) {
|
||||
syslog(LOG_ERR, "malloc: %m");
|
||||
exit(1);
|
||||
}
|
||||
curname[0] = '\0';
|
||||
|
||||
memset((char *)&his_addr, 0, sizeof(his_addr));
|
||||
addrlen = sizeof(his_addr.si_su);
|
||||
if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
|
||||
@ -506,10 +538,26 @@ main(int argc, char *argv[])
|
||||
(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
|
||||
|
||||
(void) freopen(_PATH_DEVNULL, "w", stderr);
|
||||
(void) signal(SIGPIPE, lostconn);
|
||||
(void) signal(SIGCHLD, SIG_IGN);
|
||||
if (signal(SIGURG, myoob) == SIG_ERR)
|
||||
syslog(LOG_WARNING, "signal: %m");
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
(void) sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
sa.sa_handler = sigquit;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigfillset(&sa.sa_mask); /* block all sigs in these handlers */
|
||||
(void) sigaction(SIGHUP, &sa, NULL);
|
||||
(void) sigaction(SIGINT, &sa, NULL);
|
||||
(void) sigaction(SIGQUIT, &sa, NULL);
|
||||
(void) sigaction(SIGTERM, &sa, NULL);
|
||||
sa.sa_handler = lostconn;
|
||||
(void) sigaction(SIGPIPE, &sa, NULL);
|
||||
sa.sa_handler = toolong;
|
||||
(void) sigaction(SIGALRM, &sa, NULL);
|
||||
sa.sa_handler = sigurg;
|
||||
(void) sigaction(SIGURG, &sa, NULL);
|
||||
|
||||
/* Try to handle urgent data inline */
|
||||
#ifdef SO_OOBINLINE
|
||||
@ -565,7 +613,16 @@ main(int argc, char *argv[])
|
||||
else
|
||||
reply(220, "%s FTP server (%s) ready.", hostname, version);
|
||||
|
||||
(void) setjmp(errcatch);
|
||||
if (xferlogname != NULL) {
|
||||
xferlogfd = open(xferlogname, O_WRONLY | O_APPEND | O_CREAT,
|
||||
0660);
|
||||
if (xferlogfd == -1)
|
||||
syslog(LOG_WARNING, "open xferlog `%s': %m",
|
||||
xferlogname);
|
||||
else
|
||||
doxferlog |= 2;
|
||||
}
|
||||
|
||||
ftp_loop();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
@ -579,6 +636,37 @@ lostconn(int signo)
|
||||
dologout(1);
|
||||
}
|
||||
|
||||
static void
|
||||
toolong(int signo)
|
||||
{
|
||||
|
||||
/* XXXSIGRACE */
|
||||
reply(421,
|
||||
"Timeout (" LLF " seconds): closing control connection.",
|
||||
(LLT)curclass.timeout);
|
||||
if (logging)
|
||||
syslog(LOG_INFO, "User %s timed out after " LLF " seconds",
|
||||
(pw ? pw->pw_name : "unknown"), (LLT)curclass.timeout);
|
||||
dologout(1);
|
||||
}
|
||||
|
||||
static void
|
||||
sigquit(int signo)
|
||||
{
|
||||
|
||||
if (debug)
|
||||
syslog(LOG_DEBUG, "got signal %d", signo);
|
||||
dologout(1);
|
||||
}
|
||||
|
||||
static void
|
||||
sigurg(int signo)
|
||||
{
|
||||
|
||||
urgflag = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Save the result of a getpwnam. Used for USER command, since
|
||||
* the data returned must not be clobbered by any other command
|
||||
@ -612,7 +700,6 @@ sgetpwnam(const char *name)
|
||||
static int login_attempts; /* number of failed login attempts */
|
||||
static int askpasswd; /* had USER command, ask for PASSwd */
|
||||
static int permitted; /* USER permitted */
|
||||
static char curname[LOGIN_NAME_MAX]; /* current USER name */
|
||||
|
||||
/*
|
||||
* USER command.
|
||||
@ -628,6 +715,9 @@ static char curname[LOGIN_NAME_MAX]; /* current USER name */
|
||||
void
|
||||
user(const char *name)
|
||||
{
|
||||
#ifdef LOGIN_CAP
|
||||
login_cap_t *lc = NULL;
|
||||
#endif
|
||||
char *class;
|
||||
|
||||
class = NULL;
|
||||
@ -686,13 +776,21 @@ user(const char *name)
|
||||
} else
|
||||
pw = sgetpwnam(name);
|
||||
|
||||
strlcpy(curname, name, sizeof(curname));
|
||||
strlcpy(curname, name, curname_len);
|
||||
|
||||
/* check user in /etc/ftpusers, and setup class */
|
||||
permitted = checkuser(_PATH_FTPUSERS, curname, 1, 0, &class);
|
||||
|
||||
#ifdef LOGIN_CAP /* allow login.conf configuration as well */
|
||||
if ((lc = login_getpwclass(pw)) != NULL)
|
||||
goto cleanup_user;
|
||||
#endif
|
||||
/* check user in /etc/ftpchroot */
|
||||
if (checkuser(_PATH_FTPCHROOT, curname, 0, 0, NULL)) {
|
||||
if (checkuser(_PATH_FTPCHROOT, curname, 0, 0, NULL)
|
||||
#ifdef LOGIN_CAP /* allow login.conf configuration as well */
|
||||
|| login_getcapbool(lc, "ftp-chroot", 0)
|
||||
#endif
|
||||
) {
|
||||
if (curclass.type == CLASS_GUEST) {
|
||||
syslog(LOG_NOTICE,
|
||||
"Can't change guest user to chroot class; remove entry in %s",
|
||||
@ -776,6 +874,9 @@ user(const char *name)
|
||||
}
|
||||
|
||||
cleanup_user:
|
||||
#ifdef LOGIN_CAP
|
||||
login_close(lc);
|
||||
#endif
|
||||
/*
|
||||
* Delay before reading passwd after first failed
|
||||
* attempt to slow down passwd-guessing programs.
|
||||
@ -964,10 +1065,10 @@ login_utmp(const char *line, const char *name, const char *host)
|
||||
(void)strncpy(utmpx.ut_name, name, sizeof(utmpx.ut_name));
|
||||
(void)strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
|
||||
(void)strncpy(utmpx.ut_host, host, sizeof(utmpx.ut_host));
|
||||
loginx(&utmpx);
|
||||
ftpd_loginx(&utmpx);
|
||||
}
|
||||
if (dowtmp)
|
||||
logwtmpx(line, name, host, 0, USER_PROCESS);
|
||||
ftpd_logwtmpx(line, name, host, 0, USER_PROCESS);
|
||||
#endif
|
||||
#ifdef SUPPORT_UTMP
|
||||
if (doutmp) {
|
||||
@ -976,10 +1077,10 @@ login_utmp(const char *line, const char *name, const char *host)
|
||||
(void)strncpy(utmp.ut_name, name, sizeof(utmp.ut_name));
|
||||
(void)strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
|
||||
(void)strncpy(utmp.ut_host, host, sizeof(utmp.ut_host));
|
||||
login(&utmp);
|
||||
ftpd_login(&utmp);
|
||||
}
|
||||
if (dowtmp)
|
||||
logwtmp(line, name, host);
|
||||
ftpd_logwtmp(line, name, host);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -993,15 +1094,15 @@ logout_utmp(void)
|
||||
okwtmp = logoutx(ttyline, 0, DEAD_PROCESS) & dowtmp;
|
||||
#endif
|
||||
#ifdef SUPPORT_UTMP
|
||||
okwtmp = logout(ttyline) & dowtmp;
|
||||
okwtmp = ftpd_logout(ttyline) & dowtmp;
|
||||
#endif
|
||||
}
|
||||
if (okwtmp) {
|
||||
#ifdef SUPPORT_UTMPX
|
||||
logwtmpx(ttyline, "", "", 0, DEAD_PROCESS);
|
||||
ftpd_logwtmpx(ttyline, "", "", 0, DEAD_PROCESS);
|
||||
#endif
|
||||
#ifdef SUPPORT_UTMP
|
||||
logwtmp(ttyline, "", "");
|
||||
ftpd_logwtmp(ttyline, "", "");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -1029,6 +1130,10 @@ end_login(void)
|
||||
gidcount = 0;
|
||||
curclass.type = CLASS_REAL;
|
||||
(void) seteuid((uid_t)0);
|
||||
#ifdef LOGIN_CAP
|
||||
setusercontext(NULL, getpwuid(0), (uid_t)0,
|
||||
LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK|LOGIN_SETMAC);
|
||||
#endif
|
||||
#ifdef USE_PAM
|
||||
if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
|
||||
syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
|
||||
@ -1043,6 +1148,9 @@ end_login(void)
|
||||
void
|
||||
pass(const char *passwd)
|
||||
{
|
||||
#ifdef LOGIN_CAP
|
||||
login_cap_t *lc = NULL;
|
||||
#endif
|
||||
#ifdef USE_PAM
|
||||
int e;
|
||||
#endif
|
||||
@ -1154,12 +1262,43 @@ pass(const char *passwd)
|
||||
reply(550, "Can't set gid.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
#ifdef LOGIN_CAP
|
||||
if ((lc = login_getpwclass(pw)) != NULL) {
|
||||
char remote_ip[MAXHOSTNAMELEN];
|
||||
|
||||
getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
|
||||
remote_ip, sizeof(remote_ip) - 1, NULL, 0, NI_NUMERICHOST);
|
||||
remote_ip[sizeof(remote_ip) - 1] = 0;
|
||||
if (!auth_hostok(lc, remotehost, remote_ip)) {
|
||||
syslog(LOG_INFO|LOG_AUTH,
|
||||
"FTP LOGIN FAILED (HOST) as %s: permission denied.",
|
||||
pw->pw_name);
|
||||
reply(530, "Permission denied.\n");
|
||||
pw = NULL;
|
||||
return;
|
||||
}
|
||||
if (!auth_timeok(lc, time(NULL))) {
|
||||
reply(530, "Login not available right now.\n");
|
||||
pw = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
setusercontext(lc, pw, (uid_t)0,
|
||||
LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY|LOGIN_SETRESOURCES
|
||||
|LOGIN_SETUMASK|LOGIN_SETMAC);
|
||||
#endif /*LOGIN_CAP*/
|
||||
|
||||
(void) initgroups(pw->pw_name, pw->pw_gid);
|
||||
/* cache groups for cmds.c::matchgroup() */
|
||||
gidcount = getgroups(sizeof(gidlist), gidlist);
|
||||
gidcount = getgroups(0, NULL);
|
||||
if (gidlist)
|
||||
free(gidlist);
|
||||
gidlist = malloc(gidcount * sizeof *gidlist);
|
||||
gidcount = getgroups(gidcount, gidlist);
|
||||
|
||||
#ifdef USE_PAM
|
||||
if (pamh) {
|
||||
if (pamh) {
|
||||
if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
|
||||
syslog(LOG_ERR, "pam_open_session: %s",
|
||||
pam_strerror(pamh, e));
|
||||
@ -1283,6 +1422,7 @@ pass(const char *passwd)
|
||||
}
|
||||
break;
|
||||
}
|
||||
setsid();
|
||||
setlogin(pw->pw_name);
|
||||
if (dropprivs ||
|
||||
(curclass.type != CLASS_REAL &&
|
||||
@ -1346,11 +1486,17 @@ pass(const char *passwd)
|
||||
remotehost, pw->pw_name,
|
||||
curclass.classname, CURCLASSTYPE);
|
||||
}
|
||||
#ifdef LOGIN_CAP
|
||||
login_close(lc);
|
||||
#endif
|
||||
(void) umask(curclass.umask);
|
||||
return;
|
||||
|
||||
bad:
|
||||
/* Forget all about it... */
|
||||
#ifdef LOGIN_CAP
|
||||
login_close(lc);
|
||||
#endif
|
||||
end_login();
|
||||
}
|
||||
|
||||
@ -1833,6 +1979,8 @@ send_data_with_read(int filefd, int netfd, const struct stat *st, int isdata)
|
||||
error = SS_FILE_ERROR;
|
||||
else if (write_data(netfd, buf, c, &bufrem, &then, isdata))
|
||||
error = SS_DATA_ERROR;
|
||||
else if (urgflag && handleoobcmd())
|
||||
error = SS_ABORTED;
|
||||
else
|
||||
continue;
|
||||
|
||||
@ -1899,6 +2047,8 @@ send_data_with_mmap(int filefd, int netfd, const struct stat *st, int isdata)
|
||||
isdata);
|
||||
(void) madvise(win, mapsize, MADV_DONTNEED);
|
||||
munmap(win, mapsize);
|
||||
if (urgflag && handleoobcmd())
|
||||
return (SS_ABORTED);
|
||||
if (error)
|
||||
return (SS_DATA_ERROR);
|
||||
off += mapsize;
|
||||
@ -1920,10 +2070,9 @@ send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
|
||||
{
|
||||
int c, filefd, netfd, rval;
|
||||
|
||||
urgflag = 0;
|
||||
transflag = 1;
|
||||
rval = -1;
|
||||
if (setjmp(urgcatch))
|
||||
goto cleanup_send_data;
|
||||
|
||||
switch (type) {
|
||||
|
||||
@ -1931,6 +2080,8 @@ send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
|
||||
/* XXXLUKEM: rate limit ascii send (get) */
|
||||
(void) alarm(curclass.timeout);
|
||||
while ((c = getc(instr)) != EOF) {
|
||||
if (urgflag && handleoobcmd())
|
||||
goto cleanup_send_data;
|
||||
byte_count++;
|
||||
if (c == '\n') {
|
||||
if (ferror(outstr))
|
||||
@ -1971,6 +2122,7 @@ send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
|
||||
case SS_SUCCESS:
|
||||
break;
|
||||
|
||||
case SS_ABORTED:
|
||||
case SS_NO_TRANSFER:
|
||||
goto cleanup_send_data;
|
||||
|
||||
@ -1996,11 +2148,12 @@ send_data(FILE *instr, FILE *outstr, const struct stat *st, int isdata)
|
||||
file_err:
|
||||
(void) alarm(0);
|
||||
perror_reply(551, "Error on input file");
|
||||
/* FALLTHROUGH */
|
||||
goto cleanup_send_data;
|
||||
|
||||
cleanup_send_data:
|
||||
(void) alarm(0);
|
||||
transflag = 0;
|
||||
urgflag = 0;
|
||||
if (isdata) {
|
||||
total_files_out++;
|
||||
total_files++;
|
||||
@ -2022,16 +2175,22 @@ receive_data(FILE *instr, FILE *outstr)
|
||||
int c, bare_lfs, netfd, filefd, rval;
|
||||
off_t byteswritten;
|
||||
char buf[BUFSIZ];
|
||||
struct sigaction sa, sa_saved;
|
||||
#ifdef __GNUC__
|
||||
(void) &bare_lfs;
|
||||
#endif
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = lostconn;
|
||||
(void) sigaction(SIGALRM, &sa, &sa_saved);
|
||||
|
||||
bare_lfs = 0;
|
||||
urgflag = 0;
|
||||
transflag = 1;
|
||||
rval = -1;
|
||||
byteswritten = 0;
|
||||
if (setjmp(urgcatch))
|
||||
goto cleanup_recv_data;
|
||||
|
||||
#define FILESIZECHECK(x) \
|
||||
do { \
|
||||
@ -2061,6 +2220,8 @@ receive_data(FILE *instr, FILE *outstr)
|
||||
if ((c = read(netfd, buf,
|
||||
MIN(sizeof(buf), bufrem))) <= 0)
|
||||
goto recvdone;
|
||||
if (urgflag && handleoobcmd())
|
||||
goto cleanup_recv_data;
|
||||
FILESIZECHECK(byte_count + c);
|
||||
if ((d = write(filefd, buf, c)) != c)
|
||||
goto file_err;
|
||||
@ -2079,6 +2240,8 @@ receive_data(FILE *instr, FILE *outstr)
|
||||
}
|
||||
} else {
|
||||
while ((c = read(netfd, buf, sizeof(buf))) > 0) {
|
||||
if (urgflag && handleoobcmd())
|
||||
goto cleanup_recv_data;
|
||||
FILESIZECHECK(byte_count + c);
|
||||
if (write(filefd, buf, c) != c)
|
||||
goto file_err;
|
||||
@ -2104,6 +2267,8 @@ receive_data(FILE *instr, FILE *outstr)
|
||||
(void) alarm(curclass.timeout);
|
||||
/* XXXLUKEM: rate limit ascii receive (put) */
|
||||
while ((c = getc(instr)) != EOF) {
|
||||
if (urgflag && handleoobcmd())
|
||||
goto cleanup_recv_data;
|
||||
byte_count++;
|
||||
total_data_in++;
|
||||
total_data++;
|
||||
@ -2169,7 +2334,9 @@ receive_data(FILE *instr, FILE *outstr)
|
||||
|
||||
cleanup_recv_data:
|
||||
(void) alarm(0);
|
||||
(void) sigaction(SIGALRM, &sa_saved, NULL);
|
||||
transflag = 0;
|
||||
urgflag = 0;
|
||||
total_files_in++;
|
||||
total_files++;
|
||||
total_xfers_in++;
|
||||
@ -2459,29 +2626,24 @@ fatal(const char *s)
|
||||
void
|
||||
reply(int n, const char *fmt, ...)
|
||||
{
|
||||
off_t b;
|
||||
va_list ap;
|
||||
char msg[MAXPATHLEN * 2 + 100];
|
||||
size_t b;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
b = 0;
|
||||
if (n == 0)
|
||||
cprintf(stdout, " ");
|
||||
b = snprintf(msg, sizeof(msg), " ");
|
||||
else if (n < 0)
|
||||
cprintf(stdout, "%d-", -n);
|
||||
b = snprintf(msg, sizeof(msg), "%d-", -n);
|
||||
else
|
||||
cprintf(stdout, "%d ", n);
|
||||
b = vprintf(fmt, ap);
|
||||
b = snprintf(msg, sizeof(msg), "%d ", n);
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg + b, sizeof(msg) - b, fmt, ap);
|
||||
va_end(ap);
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
cprintf(stdout, "\r\n");
|
||||
cprintf(stdout, "%s\r\n", msg);
|
||||
(void)fflush(stdout);
|
||||
if (debug) {
|
||||
syslog(LOG_DEBUG, "<--- %d%c", abs(n), (n < 0) ? '-' : ' ');
|
||||
va_start(ap, fmt);
|
||||
vsyslog(LOG_DEBUG, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
if (debug)
|
||||
syslog(LOG_DEBUG, "<--- %s", msg);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2503,6 +2665,8 @@ logremotehost(struct sockinet *who)
|
||||
|
||||
/*
|
||||
* Record logout in wtmp file and exit with supplied status.
|
||||
* NOTE: because this is called from signal handlers it cannot
|
||||
* use stdio (or call other functions that use stdio).
|
||||
*/
|
||||
void
|
||||
dologout(int status)
|
||||
@ -2520,6 +2684,8 @@ dologout(int status)
|
||||
#endif
|
||||
}
|
||||
/* beware of flushing buffers after a SIGPIPE */
|
||||
if (xferlogfd != -1)
|
||||
close(xferlogfd);
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
@ -2527,17 +2693,21 @@ void
|
||||
abor(void)
|
||||
{
|
||||
|
||||
if (!transflag)
|
||||
return;
|
||||
tmpline[0] = '\0';
|
||||
is_oob = 0;
|
||||
reply(426, "Transfer aborted. Data connection closed.");
|
||||
reply(226, "Abort successful");
|
||||
longjmp(urgcatch, 1);
|
||||
transflag = 0; /* flag that the transfer has aborted */
|
||||
}
|
||||
|
||||
void
|
||||
statxfer(void)
|
||||
{
|
||||
|
||||
if (!transflag)
|
||||
return;
|
||||
tmpline[0] = '\0';
|
||||
is_oob = 0;
|
||||
if (file_size != (off_t) -1)
|
||||
@ -2550,22 +2720,39 @@ statxfer(void)
|
||||
(LLT)byte_count, PLURAL(byte_count));
|
||||
}
|
||||
|
||||
static void
|
||||
myoob(int signo)
|
||||
/*
|
||||
* Call when urgflag != 0 to handle Out Of Band commands.
|
||||
* Returns non zero if the OOB command aborted the transfer
|
||||
* by setting transflag to 0. (c.f., "ABOR").
|
||||
*/
|
||||
static int
|
||||
handleoobcmd()
|
||||
{
|
||||
char *cp;
|
||||
|
||||
if (!urgflag)
|
||||
return (0);
|
||||
urgflag = 0;
|
||||
/* only process if transfer occurring */
|
||||
if (!transflag)
|
||||
return;
|
||||
return (0);
|
||||
cp = tmpline;
|
||||
if (getline(cp, sizeof(tmpline), stdin) == NULL) {
|
||||
reply(221, "You could at least say goodbye.");
|
||||
dologout(0);
|
||||
}
|
||||
is_oob = 1;
|
||||
ftp_handle_line(cp);
|
||||
is_oob = 0;
|
||||
/*
|
||||
* Manually parse OOB commands, because we can't
|
||||
* recursively call the yacc parser...
|
||||
*/
|
||||
if (strcasecmp(cp, "ABOR\r\n") == 0) {
|
||||
abor();
|
||||
} else if (strcasecmp(cp, "STAT\r\n") == 0) {
|
||||
statxfer();
|
||||
} else {
|
||||
/* XXX: error with "500 unknown command" ? */
|
||||
}
|
||||
return (transflag == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -2981,7 +3168,8 @@ send_file_list(const char *whichf)
|
||||
DIR *dirp = NULL;
|
||||
struct dirent *dir;
|
||||
FILE *dout = NULL;
|
||||
char **dirlist, *dirname, *notglob, *p;
|
||||
char **dirlist, *dirname, *p;
|
||||
char *notglob = NULL;
|
||||
int simple = 0;
|
||||
int freeglob = 0;
|
||||
glob_t gl;
|
||||
@ -2992,6 +3180,7 @@ send_file_list(const char *whichf)
|
||||
(void) &simple;
|
||||
(void) &freeglob;
|
||||
#endif
|
||||
urgflag = 0;
|
||||
|
||||
p = NULL;
|
||||
if (strpbrk(whichf, "~{[*?") != NULL) {
|
||||
@ -3001,11 +3190,11 @@ send_file_list(const char *whichf)
|
||||
freeglob = 1;
|
||||
if (glob(whichf, flags, 0, &gl)) {
|
||||
reply(550, "not found");
|
||||
goto out;
|
||||
goto cleanup_send_file_list;
|
||||
} else if (gl.gl_pathc == 0) {
|
||||
errno = ENOENT;
|
||||
perror_reply(550, whichf);
|
||||
goto out;
|
||||
goto cleanup_send_file_list;
|
||||
}
|
||||
dirlist = gl.gl_pathv;
|
||||
} else {
|
||||
@ -3016,10 +3205,6 @@ send_file_list(const char *whichf)
|
||||
}
|
||||
/* XXX: } for vi sm */
|
||||
|
||||
if (setjmp(urgcatch)) {
|
||||
transflag = 0;
|
||||
goto out;
|
||||
}
|
||||
while ((dirname = *dirlist++) != NULL) {
|
||||
int trailingslash = 0;
|
||||
|
||||
@ -3035,7 +3220,7 @@ send_file_list(const char *whichf)
|
||||
|
||||
argv[1] = dirname;
|
||||
retrieve(argv, dirname);
|
||||
goto out;
|
||||
goto cleanup_send_file_list;
|
||||
}
|
||||
perror_reply(550, whichf);
|
||||
goto cleanup_send_file_list;
|
||||
@ -3050,8 +3235,8 @@ send_file_list(const char *whichf)
|
||||
if (dout == NULL) {
|
||||
dout = dataconn("file list", (off_t)-1, "w");
|
||||
if (dout == NULL)
|
||||
goto out;
|
||||
transflag++;
|
||||
goto cleanup_send_file_list;
|
||||
transflag = 1;
|
||||
}
|
||||
cprintf(dout, "%s%s\n", dirname,
|
||||
type == TYPE_A ? "\r" : "");
|
||||
@ -3068,6 +3253,9 @@ send_file_list(const char *whichf)
|
||||
while ((dir = readdir(dirp)) != NULL) {
|
||||
char nbuf[MAXPATHLEN];
|
||||
|
||||
if (urgflag && handleoobcmd())
|
||||
goto cleanup_send_file_list;
|
||||
|
||||
if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
|
||||
continue;
|
||||
|
||||
@ -3090,8 +3278,8 @@ send_file_list(const char *whichf)
|
||||
dout = dataconn("file list", (off_t)-1,
|
||||
"w");
|
||||
if (dout == NULL)
|
||||
goto out;
|
||||
transflag++;
|
||||
goto cleanup_send_file_list;
|
||||
transflag = 1;
|
||||
}
|
||||
p = nbuf;
|
||||
if (nbuf[0] == '.' && nbuf[1] == '/')
|
||||
@ -3111,9 +3299,9 @@ send_file_list(const char *whichf)
|
||||
reply(226, "Transfer complete.");
|
||||
|
||||
cleanup_send_file_list:
|
||||
transflag = 0;
|
||||
closedataconn(dout);
|
||||
out:
|
||||
transflag = 0;
|
||||
urgflag = 0;
|
||||
total_xfers++;
|
||||
total_xfers_out++;
|
||||
if (notglob)
|
||||
@ -3144,7 +3332,7 @@ conffilename(const char *s)
|
||||
* if error != NULL, append ": " + error
|
||||
*
|
||||
* if doxferlog != 0, bytes != -1, and command is "get", "put",
|
||||
* or "append", syslog a wu-ftpd style xferlog entry
|
||||
* or "append", syslog and/or write a wu-ftpd style xferlog entry
|
||||
*/
|
||||
void
|
||||
logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
|
||||
@ -3187,7 +3375,6 @@ logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
|
||||
syslog(LOG_INFO, "%s", buf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* syslog wu-ftpd style log entry, prefixed with "xferlog: "
|
||||
*/
|
||||
@ -3202,21 +3389,15 @@ logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
|
||||
return;
|
||||
|
||||
time(&now);
|
||||
syslog(LOG_INFO,
|
||||
"xferlog%s: %.24s %ld %s " LLF " %s %c %s %c %c %s FTP 0 * %c",
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"%.24s %ld %s " LLF " %s %c %s %c %c %s FTP 0 * %c\n",
|
||||
|
||||
/*
|
||||
* XXX: wu-ftpd puts (send) or (recv) in the syslog message, and removes
|
||||
* XXX: wu-ftpd puts ' (send)' or ' (recv)' in the syslog message, and removes
|
||||
* the full date. This may be problematic for accurate log parsing,
|
||||
* given that syslog messages don't contain the full date.
|
||||
*/
|
||||
#if 1 /* lukem's method; easier to convert to actual xferlog file */
|
||||
"",
|
||||
ctime(&now),
|
||||
#else /* wu-ftpd's syslog method, with an extra unneeded space */
|
||||
(direction == 'i') ? " (recv)" : " (send)",
|
||||
"",
|
||||
#endif
|
||||
elapsed == NULL ? 0 : elapsed->tv_sec + (elapsed->tv_usec > 0),
|
||||
remotehost,
|
||||
(LLT) bytes,
|
||||
@ -3232,6 +3413,13 @@ logxfer(const char *command, off_t bytes, const char *file1, const char *file2,
|
||||
curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name,
|
||||
error != NULL ? 'i' : 'c'
|
||||
);
|
||||
|
||||
if ((doxferlog & 2) && xferlogfd != -1)
|
||||
write(xferlogfd, buf, len);
|
||||
if ((doxferlog & 1)) {
|
||||
buf[len-1] = '\n'; /* strip \n from syslog message */
|
||||
syslog(LOG_INFO, "xferlog: %s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user