From 35a9f7f898569b71a98f77b369385496f8371a01 Mon Sep 17 00:00:00 2001 From: obrien Date: Wed, 18 Aug 2004 06:34:20 +0000 Subject: Merge rev 1.2 (OPIE, login user capabilities database, PAM) support into 'nbsd_20040809'. --- contrib/lukemftpd/src/ftpd.c | 356 +++++++++++++++++++++++++++++++++---------- 1 file changed, 272 insertions(+), 84 deletions(-) (limited to 'contrib/lukemftpd') diff --git a/contrib/lukemftpd/src/ftpd.c b/contrib/lukemftpd/src/ftpd.c index b623919..858036e 100644 --- a/contrib/lukemftpd/src/ftpd.c +++ b/contrib/lukemftpd/src/ftpd.c @@ -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 #include #include -#include #include #include #include @@ -169,14 +164,19 @@ __FBSDID("$FreeBSD$"); #include #include #endif +#ifdef LOGIN_CAP +#include +#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); + } } /* -- cgit v1.1