Merge rev 1.2 (OPIE, login user capabilities database, PAM) support

into 'nbsd_20040809'.
This commit is contained in:
David E. O'Brien 2004-08-18 06:34:20 +00:00
parent 07539dd5d7
commit 5bf06d869f

View File

@ -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);
}
}
/*