diff --git a/libexec/ftpd/cmds.c b/libexec/ftpd/cmds.c index 9525fe05cfe6..e083b60bcb5b 100644 --- a/libexec/ftpd/cmds.c +++ b/libexec/ftpd/cmds.c @@ -1,4 +1,4 @@ -/* $NetBSD: cmds.c,v 1.9 2000/12/04 10:50:39 itojun Exp $ */ +/* $NetBSD: cmds.c,v 1.10 2000/12/18 02:32:50 lukem Exp $ */ /* * Copyright (c) 1999-2000 The NetBSD Foundation, Inc. @@ -101,7 +101,7 @@ #include #ifndef lint -__RCSID("$NetBSD: cmds.c,v 1.9 2000/12/04 10:50:39 itojun Exp $"); +__RCSID("$NetBSD: cmds.c,v 1.10 2000/12/18 02:32:50 lukem Exp $"); #endif /* not lint */ #include @@ -188,7 +188,7 @@ delete(const char *name) perror_reply(550, name); } else ack("DELE"); - logcmd("delete", -1, name, NULL, NULL, p); + logxfer("delete", -1, name, NULL, NULL, p); } void @@ -219,7 +219,7 @@ makedir(const char *name) perror_reply(550, name); } else replydirname(name, "directory created."); - logcmd("mkdir", -1, name, NULL, NULL, p); + logxfer("mkdir", -1, name, NULL, NULL, p); } void @@ -412,7 +412,7 @@ removedir(const char *name) perror_reply(550, name); } else ack("RMD"); - logcmd("rmdir", -1, name, NULL, NULL, p); + logxfer("rmdir", -1, name, NULL, NULL, p); } char * @@ -438,7 +438,7 @@ renamecmd(const char *from, const char *to) perror_reply(550, "rename"); } else ack("RNTO"); - logcmd("rename", -1, from, to, NULL, p); + logxfer("rename", -1, from, to, NULL, p); } void @@ -446,14 +446,17 @@ sizecmd(const char *filename) { switch (type) { case TYPE_L: - case TYPE_I: { + case TYPE_I: + { struct stat stbuf; if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) reply(550, "%s: not a plain file.", filename); else reply(213, ULLF, (ULLT)stbuf.st_size); - break; } - case TYPE_A: { + break; + } + case TYPE_A: + { FILE *fin; int c; off_t count; @@ -478,7 +481,8 @@ sizecmd(const char *filename) (void) fclose(fin); reply(213, LLF, (LLT)count); - break; } + break; + } default: reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); } diff --git a/libexec/ftpd/conf.c b/libexec/ftpd/conf.c index afafa9eeb694..53b9c9e2505b 100644 --- a/libexec/ftpd/conf.c +++ b/libexec/ftpd/conf.c @@ -1,4 +1,4 @@ -/* $NetBSD: conf.c,v 1.36 2000/11/16 13:15:13 lukem Exp $ */ +/* $NetBSD: conf.c,v 1.37 2000/12/18 02:32:50 lukem Exp $ */ /*- * Copyright (c) 1997-2000 The NetBSD Foundation, Inc. @@ -38,17 +38,19 @@ #include #ifndef lint -__RCSID("$NetBSD: conf.c,v 1.36 2000/11/16 13:15:13 lukem Exp $"); +__RCSID("$NetBSD: conf.c,v 1.37 2000/12/18 02:32:50 lukem Exp $"); #endif /* not lint */ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -71,6 +73,13 @@ static char *strend(const char *, char *); static int filetypematch(char *, int); + /* class defaults */ +#define DEFAULT_LIMIT -1 /* unlimited connections */ +#define DEFAULT_MAXFILESIZE -1 /* unlimited file size */ +#define DEFAULT_MAXTIMEOUT 7200 /* 2 hours */ +#define DEFAULT_TIMEOUT 900 /* 15 minutes */ +#define DEFAULT_UMASK 027 /* 15 minutes */ + /* * Initialise curclass to an `empty' state */ @@ -88,26 +97,28 @@ init_curclass(void) free(conv); } + memset((char *)&curclass.advertise, 0, sizeof(curclass.advertise)); + curclass.advertise.su_len = 0; /* `not used' */ REASSIGN(curclass.chroot, NULL); REASSIGN(curclass.classname, NULL); curclass.conversions = NULL; REASSIGN(curclass.display, NULL); REASSIGN(curclass.homedir, NULL); - curclass.limit = -1; /* unlimited connections */ + curclass.limit = DEFAULT_LIMIT; REASSIGN(curclass.limitfile, NULL); - curclass.maxfilesize = -1; /* unlimited file size */ + curclass.maxfilesize = DEFAULT_MAXFILESIZE; curclass.maxrateget = 0; curclass.maxrateput = 0; - curclass.maxtimeout = 7200; /* 2 hours */ + curclass.maxtimeout = DEFAULT_MAXTIMEOUT; REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG)); REASSIGN(curclass.notify, NULL); curclass.portmin = 0; curclass.portmax = 0; curclass.rateget = 0; curclass.rateput = 0; - curclass.timeout = 900; /* 15 minutes */ + curclass.timeout = DEFAULT_TIMEOUT; /* curclass.type is set elsewhere */ - curclass.umask = 027; + curclass.umask = DEFAULT_UMASK; CURCLASS_FLAGS_SET(checkportcmd); CURCLASS_FLAGS_SET(modify); @@ -137,6 +148,7 @@ parse_conf(const char *findclass) init_curclass(); REASSIGN(curclass.classname, xstrdup(findclass)); + /* set more guest defaults */ if (strcasecmp(findclass, "guest") == 0) { CURCLASS_FLAGS_CLR(modify); curclass.umask = 0707; @@ -192,7 +204,58 @@ parse_conf(const char *findclass) REASSIGN(curclass.x, arg); \ } while (0) - if (strcasecmp(word, "checkportcmd") == 0) { + + if (0) { + /* no-op */ + + } else if (strcasecmp(word, "advertise") == 0) { + struct addrinfo hints, *res; + int error; + + memset((char *)&curclass.advertise, 0, + sizeof(curclass.advertise)); + curclass.advertise.su_len = 0; + if (none || EMPTYSTR(arg)) + continue; + res = NULL; + memset(&hints, 0, sizeof(hints)); + /* + * only get addresses of the family + * that we're listening on + */ + hints.ai_family = ctrl_addr.su_family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(arg, "0", &hints, &res); + if (error) { + syslog(LOG_WARNING, "%s line %d: %s", + infile, (int)line, gai_strerror(error)); + advertiseparsefail: + if (res) + freeaddrinfo(res); + continue; + } + if (res->ai_next) { + syslog(LOG_WARNING, + "%s line %d: multiple addresses returned for `%s'; please be more specific", + infile, (int)line, arg); + goto advertiseparsefail; + } + if (sizeof(curclass.advertise) < res->ai_addrlen || ( +#ifdef INET6 + res->ai_family != AF_INET6 && +#endif + res->ai_family != AF_INET)) { + syslog(LOG_WARNING, + "%s line %d: unsupported protocol %d for `%s'", + infile, (int)line, res->ai_family, arg); + goto advertiseparsefail; + } + memcpy(&curclass.advertise, res->ai_addr, + res->ai_addrlen); + curclass.advertise.su_len = res->ai_addrlen; + freeaddrinfo(res); + + } else if (strcasecmp(word, "checkportcmd") == 0) { CONF_FLAG(checkportcmd); } else if (strcasecmp(word, "chroot") == 0) { @@ -272,21 +335,11 @@ parse_conf(const char *findclass) } else if (strcasecmp(word, "homedir") == 0) { CONF_STRING(homedir); - } else if (strcasecmp(word, "maxfilesize") == 0) { - if (none || EMPTYSTR(arg)) - continue; - llval = strsuftoll(arg); - if (llval == -1) { - syslog(LOG_WARNING, - "%s line %d: invalid maxfilesize %s", - infile, (int)line, arg); - continue; - } - curclass.maxfilesize = llval; - } else if (strcasecmp(word, "limit") == 0) { int limit; + curclass.limit = DEFAULT_LIMIT; + REASSIGN(curclass.limitfile, NULL); if (none || EMPTYSTR(arg)) continue; limit = (int)strtol(arg, &endp, 10); @@ -300,7 +353,21 @@ parse_conf(const char *findclass) REASSIGN(curclass.limitfile, EMPTYSTR(p) ? NULL : xstrdup(p)); + } else if (strcasecmp(word, "maxfilesize") == 0) { + curclass.maxfilesize = DEFAULT_MAXFILESIZE; + if (none || EMPTYSTR(arg)) + continue; + llval = strsuftoll(arg); + if (llval == -1) { + syslog(LOG_WARNING, + "%s line %d: invalid maxfilesize %s", + infile, (int)line, arg); + continue; + } + curclass.maxfilesize = llval; + } else if (strcasecmp(word, "maxtimeout") == 0) { + curclass.maxtimeout = DEFAULT_MAXTIMEOUT; if (none || EMPTYSTR(arg)) continue; timeout = (unsigned int)strtoul(arg, &endp, 10); @@ -341,12 +408,9 @@ parse_conf(const char *findclass) int minport, maxport; char *min, *max; - if (none) { - curclass.portmin = 0; - curclass.portmax = 0; - continue; - } - if (EMPTYSTR(arg)) + curclass.portmin = 0; + curclass.portmax = 0; + if (none || EMPTYSTR(arg)) continue; min = arg; NEXTWORD(p, max); @@ -382,6 +446,8 @@ parse_conf(const char *findclass) curclass.portmax = maxport; } else if (strcasecmp(word, "rateget") == 0) { + curclass.maxrateget = 0; + curclass.rateget = 0; if (none || EMPTYSTR(arg)) continue; llval = strsuftoll(arg); @@ -395,6 +461,8 @@ parse_conf(const char *findclass) curclass.rateget = llval; } else if (strcasecmp(word, "rateput") == 0) { + curclass.maxrateput = 0; + curclass.rateput = 0; if (none || EMPTYSTR(arg)) continue; llval = strsuftoll(arg); @@ -411,6 +479,7 @@ parse_conf(const char *findclass) CONF_FLAG(sanenames); } else if (strcasecmp(word, "timeout") == 0) { + curclass.timeout = DEFAULT_TIMEOUT; if (none || EMPTYSTR(arg)) continue; timeout = (unsigned int)strtoul(arg, &endp, 10); @@ -443,6 +512,7 @@ parse_conf(const char *findclass) } else if (strcasecmp(word, "umask") == 0) { mode_t umask; + curclass.umask = DEFAULT_UMASK; if (none || EMPTYSTR(arg)) continue; umask = (mode_t)strtoul(arg, &endp, 8); @@ -472,8 +542,9 @@ parse_conf(const char *findclass) /* * Show file listed in curclass.display first time in, and list all the - * files named in curclass.notify in the current directory. Send back - * responses with the prefix `code' + "-". + * files named in curclass.notify in the current directory. + * Send back responses with the prefix `code' + "-". + * If code == -1, flush the internal cache of directory names and return. */ void show_chdir_messages(int code) @@ -488,6 +559,13 @@ show_chdir_messages(int code) char cwd[MAXPATHLEN]; char *cp, **rlist; + if (code == -1) { + if (slist != NULL) + sl_free(slist, 1); + slist = NULL; + return; + } + if (quietmessages) return; @@ -669,7 +747,6 @@ format_path(char *dst, const char *src) len = 0; if (src == NULL) return; - for (p = src; *p && len < MAXPATHLEN; p++) { if (*p == '%') { p++; diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index 348795f3976e..7cc3fa12c0bd 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -1,4 +1,4 @@ -/* $NetBSD: extern.h,v 1.36 2000/11/30 02:59:11 lukem Exp $ */ +/* $NetBSD: extern.h,v 1.37 2000/12/18 02:32:51 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -138,7 +138,7 @@ int ftpd_pclose(FILE *); FILE *ftpd_popen(char *[], const char *, int); char *getline(char *, int, FILE *); void init_curclass(void); -void logcmd(const char *, off_t, const char *, const char *, +void logxfer(const char *, off_t, const char *, const char *, const struct timeval *, const char *); void logwtmp(const char *, const char *, const char *); struct tab *lookup(struct tab *, const char *); @@ -175,6 +175,38 @@ void user(const char *); char *xstrdup(const char *); void yyerror(char *); +#include + +#ifdef BSD4_4 +# define HAVE_SETPROCTITLE 1 +# define HAVE_SOCKADDR_SA_LEN 1 +#endif + +struct sockinet { + union sockunion { + struct sockaddr_in su_sin; +#ifdef INET6 + struct sockaddr_in6 su_sin6; +#endif + } si_su; +#if !HAVE_SOCKADDR_SA_LEN + int si_len; +#endif +}; + +#if !HAVE_SOCKADDR_SA_LEN +# define su_len si_len +#else +# define su_len si_su.su_sin.sin_len +#endif +#define su_addr si_su.su_sin.sin_addr +#define su_family si_su.su_sin.sin_family +#define su_port si_su.su_sin.sin_port +#ifdef INET6 +# define su_6addr si_su.su_sin6.sin6_addr +# define su_scope_id si_su.su_sin6.sin6_scope_id +#endif + struct tab { char *name; short token; @@ -213,6 +245,7 @@ typedef enum { #define CURCLASS_FLAGS_ISSET(x) (curclass.flags & (FLAG_ ## x)) struct ftpclass { + struct sockinet advertise; /* PASV address to advertise as */ char *chroot; /* Directory to chroot(2) to at login */ char *classname; /* Current class */ struct ftpconv *conversions; /* List of conversions */ @@ -236,38 +269,6 @@ struct ftpclass { mode_t umask; /* Umask to use */ }; -#include - -#ifdef BSD4_4 -# define HAVE_SETPROCTITLE 1 -# define HAVE_SOCKADDR_SA_LEN 1 -#endif - -struct sockinet { - union sockunion { - struct sockaddr_in su_sin; -#ifdef INET6 - struct sockaddr_in6 su_sin6; -#endif - } si_su; -#if !HAVE_SOCKADDR_SA_LEN - int si_len; -#endif -}; - -#if !HAVE_SOCKADDR_SA_LEN -# define su_len si_len -#else -# define su_len si_su.su_sin.sin_len -#endif -#define su_addr si_su.su_sin.sin_addr -#define su_family si_su.su_sin.sin_family -#define su_port si_su.su_sin.sin_port -#ifdef INET6 -# define su_6addr si_su.su_sin6.sin6_addr -# define su_scope_id si_su.su_sin6.sin6_scope_id -#endif - extern int yyparse(void); #ifndef GLOBAL diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y index dbbfbdd2dff3..ba014654b260 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -1,4 +1,4 @@ -/* $NetBSD: ftpcmd.y,v 1.58 2000/11/30 02:59:11 lukem Exp $ */ +/* $NetBSD: ftpcmd.y,v 1.59 2000/12/18 02:32:51 lukem Exp $ */ /*- * Copyright (c) 1997-2000 The NetBSD Foundation, Inc. @@ -83,7 +83,7 @@ #if 0 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #else -__RCSID("$NetBSD: ftpcmd.y,v 1.58 2000/11/30 02:59:11 lukem Exp $"); +__RCSID("$NetBSD: ftpcmd.y,v 1.59 2000/12/18 02:32:51 lukem Exp $"); #endif #endif /* not lint */ @@ -237,7 +237,7 @@ cmd reply(221, "Thank you for using the FTP service on %s.", hostname); - if (logged_in) { + if (logged_in && logging) { syslog(LOG_INFO, "Data traffic: " LLF " byte%s in " LLF " file%s", (LLT)total_data, PLURAL(total_data), @@ -862,7 +862,7 @@ rcmd { if ($2) { fromname = NULL; - restart_point = $4; /* XXX $4 is only "int" */ + restart_point = $4; /* XXX: $4 is only "int" */ reply(350, "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", (LLT)restart_point); @@ -955,7 +955,7 @@ host_long_port6 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; if (his_addr.su_family == AF_INET6) { - /* XXX more sanity checks! */ + /* XXX: more sanity checks! */ data_dest.su_scope_id = his_addr.su_scope_id; } #else diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 8f0a733ee345..65f993a484a8 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -1,4 +1,4 @@ -.\" $NetBSD: ftpd.8,v 1.62 2000/12/01 07:59:47 lukem Exp $ +.\" $NetBSD: ftpd.8,v 1.63 2000/12/18 02:32:51 lukem Exp $ .\" .\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -67,7 +67,7 @@ .\" .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" -.Dd December 1, 2000 +.Dd December 18, 2000 .Dt FTPD 8 .Os .Sh NAME @@ -76,7 +76,7 @@ Internet File Transfer Protocol server .Sh SYNOPSIS .Nm -.Op Fl dHlqQrsuUwW +.Op Fl dHlqQrsuUwWX .Op Fl a Ar anondir .Op Fl c Ar confdir .Op Fl C Ar user @@ -103,11 +103,24 @@ as the directory to .Xr chroot 2 into for anonymous logins. Default is the home directory for the ftp user. +This can also be specified with the +.Xr ftpd.conf 5 +.Sy chroot +directive. .It Fl c Ar confdir Change the root directory of the configuration files from .Dq Pa /etc to .Ar confdir . +This changes the directory for the following files: +.Pa /etc/ftpchroot , +.Pa /etc/ftpusers , +.Pa /etc/ftpwelcome , +.Pa /etc/motd , +and the file specified by the +.Xr ftpd.conf 5 +.Sy limit +directive. .It Fl C Ar user Check whether .Ar user @@ -119,7 +132,7 @@ and exit without attempting a connection. exits with an exit code of 0 if access would be granted, or 1 otherwise. This can be useful for testing configurations. .It Fl d -Debugging information is written to the syslog using +Debugging information is written to the syslog using a facility of .Dv LOG_FTP . .It Fl e Ar emailaddr Use @@ -156,8 +169,8 @@ Each successful and failed .Tn FTP session is logged using syslog with a facility of .Dv LOG_FTP . -If this option is specified twice, the retrieve (get), store (put), append, -delete, make directory, remove directory and rename operations and +If this option is specified more than once, the retrieve (get), store (put), +append, delete, make directory, remove directory and rename operations and their file name arguments are also logged. .It Fl P Ar dataport Use @@ -228,6 +241,23 @@ Don't log each .Tn FTP session to .Pa /var/log/wtmp . +.It Fl X +Log +.Tn wu-ftpd +style +.Sq xferlog +entries to the syslog, prefixed with +.Dq "xferlog:\ " , +using a facility of +.Dv LOG_FTP . +These syslog entries can be converted to a +.Tn wu-ftpd +style +.Pa xferlog +file suitable for input into a third-party log analysis tool with a command +similar to: +.Dl "grep 'xferlog: ' /var/log/xferlog | \e" +.Dl "\ \ \ sed -e 's/^.*xferlog: //' > wuxferlog" .El .Pp The file @@ -247,13 +277,13 @@ prints it before issuing the message. If the file .Pa /etc/motd -exists, +exists (under the chroot directory if applicable), .Nm prints it after a successful login. -(This may be changed with the +This may be changed with the .Xr ftpd.conf 5 directive -.Sy upload . ) +.Sy motd . .Pp The .Nm diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 69b99469bef7..9eeab12bc851 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -1,4 +1,4 @@ -/* $NetBSD: ftpd.c,v 1.117 2000/11/30 08:33:33 lukem Exp $ */ +/* $NetBSD: ftpd.c,v 1.118 2000/12/18 02:32:51 lukem Exp $ */ /* * Copyright (c) 1997-2000 The NetBSD Foundation, Inc. @@ -109,7 +109,7 @@ __COPYRIGHT( #if 0 static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; #else -__RCSID("$NetBSD: ftpd.c,v 1.117 2000/11/30 08:33:33 lukem Exp $"); +__RCSID("$NetBSD: ftpd.c,v 1.118 2000/12/18 02:32:51 lukem Exp $"); #endif #endif /* not lint */ @@ -150,6 +150,7 @@ __RCSID("$NetBSD: ftpd.c,v 1.117 2000/11/30 08:33:33 lukem Exp $"); #include #include #include +#include #include #include #include @@ -176,6 +177,7 @@ 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 dropprivs; /* if privileges should or have been dropped */ int mapped; /* IPv4 connection on AF_INET6 socket */ off_t file_size; @@ -211,10 +213,10 @@ static int bind_pasv_addr(void); static int checkuser(const char *, const char *, int, int, char **); static int checkaccess(const char *); static int checkpassword(const struct passwd *, const char *); -static void dolog(struct sockinet *); static void end_login(void); static FILE *getdatasock(const char *); static char *gunique(const char *); +static void logremotehost(struct sockinet *); static void lostconn(int); static void myoob(int); static int receive_data(FILE *, FILE *); @@ -248,8 +250,9 @@ main(int argc, char *argv[]) sflag = 0; dataport = 0; dopidfile = 1; /* default: DO use a pid file to count users */ - doutmp = 0; /* default: don't log to utmp */ + doutmp = 0; /* default: Do NOT log to utmp */ dowtmp = 1; /* default: DO log to wtmp */ + doxferlog = 0; /* default: Do NOT syslog xferlog */ dropprivs = 0; mapped = 0; usedefault = 1; @@ -265,7 +268,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:wW")) + while ((ch = getopt(argc, argv, "a:c:C:de:h:HlP:qQrst:T:uUvV:wWX")) != -1) { switch (ch) { case 'a': @@ -360,6 +363,10 @@ main(int argc, char *argv[]) dowtmp = 0; break; + case 'X': + doxferlog = 1; + break; + default: if (optopt == 'a' || optopt == 'C') exit(1); @@ -370,14 +377,14 @@ main(int argc, char *argv[]) if (EMPTYSTR(confdir)) confdir = _DEFAULT_CONFDIR; - memset((char *)&his_addr, 0, sizeof (his_addr)); + 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) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } his_addr.su_len = addrlen; - memset((char *)&ctrl_addr, 0, sizeof (ctrl_addr)); + memset((char *)&ctrl_addr, 0, sizeof(ctrl_addr)); addrlen = sizeof(ctrl_addr.si_su); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); @@ -472,7 +479,7 @@ main(int argc, char *argv[]) if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif - dolog(&his_addr); + logremotehost(&his_addr); /* * Set up default state */ @@ -664,7 +671,7 @@ user(const char *name) * * NOTE: needs struct passwd *pw setup before use. */ -int +static int checkuser(const char *fname, const char *name, int def, int nofile, char **retclass) { @@ -790,7 +797,7 @@ checkuser(const char *fname, const char *name, int def, int nofile, * * NOTE: needs struct passwd *pw setup (for checkuser()) */ -int +static int checkaccess(const char *name) { @@ -798,7 +805,7 @@ checkaccess(const char *name) } /* - * Terminate login as previous user, if any, resetting state; + * Terminate login as previous user (if any), resetting state; * used when USER command is given or login fails. */ static void @@ -812,12 +819,15 @@ end_login(void) logout(utmp.ut_line); } /* reset login state */ - (void) seteuid((uid_t)0); + show_chdir_messages(-1); /* flush chdir cache */ + if (pw != NULL && pw->pw_passwd != NULL) + memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); pw = NULL; logged_in = 0; quietmessages = 0; gidcount = 0; curclass.type = CLASS_REAL; + (void) seteuid((uid_t)0); } void @@ -968,6 +978,8 @@ pass(const char *passwd) class = xstrdup("real"); break; default: + syslog(LOG_ERR, "unknown curclass.type %d; aborting", + curclass.type); abort(); } } @@ -1047,6 +1059,16 @@ pass(const char *passwd) } break; case CLASS_REAL: + /* only chroot REAL if explictly requested */ + if (! EMPTYSTR(curclass.chroot)) { + format_path(root, curclass.chroot); + if (EMPTYSTR(root) || chroot(root) < 0) { + syslog(LOG_NOTICE, + "REAL user %s: can't chroot to %s: %m", + pw->pw_name, root); + goto bad_chroot; + } + } format_path(homedir, curclass.homedir ? curclass.homedir : pw->pw_dir); @@ -1100,6 +1122,8 @@ pass(const char *passwd) (void)display_file(conffilename(curclass.motd), 230); show_chdir_messages(230); if (curclass.type == CLASS_GUEST) { + char *p; + reply(230, "Guest login ok, access restrictions apply."); #if HAVE_SETPROCTITLE snprintf(proctitle, sizeof(proctitle), @@ -1113,6 +1137,11 @@ pass(const char *passwd) "ANONYMOUS FTP LOGIN FROM %s, %s (class: %s, type: %s)", remotehost, passwd, curclass.classname, CURCLASSTYPE); + /* store guest password reply into pw_passwd */ + REASSIGN(pw->pw_passwd, xstrdup(passwd)); + for (p = pw->pw_passwd; *p; p++) + if (!isgraph(*p)) + *p = '_'; } else { reply(230, "User %s logged in.", pw->pw_name); #if HAVE_SETPROCTITLE @@ -1153,16 +1182,16 @@ retrieve(char *argv[], const char *name) tdp = NULL; dispname = name; fin = dout = NULL; - if (argv == NULL) { + if (argv == NULL) { /* if not running a command ... */ log = 1; isdata = 1; fin = fopen(name, "r"); closefunc = fclose; - if (fin == NULL) + if (fin == NULL) /* doesn't exist?; try a conversion */ argv = do_conversion(name); if (argv != NULL) { isconversion++; - syslog(LOG_INFO, "get command: '%s' on '%s'", + syslog(LOG_DEBUG, "get command: '%s' on '%s'", argv[0], name); } } @@ -1188,7 +1217,7 @@ retrieve(char *argv[], const char *name) if (errno != 0) { perror_reply(550, dispname); if (log) - logcmd("get", -1, name, NULL, NULL, + logxfer("get", -1, name, NULL, NULL, strerror(errno)); } goto cleanupretrieve; @@ -1230,7 +1259,7 @@ retrieve(char *argv[], const char *name) tdp = &td; done: if (log) - logcmd("get", byte_count, name, NULL, tdp, NULL); + logxfer("get", byte_count, name, NULL, tdp, NULL); closerv = (*closefunc)(fin); if (sendrv == 0) { FILE *err; @@ -1284,7 +1313,8 @@ store(const char *name, const char *mode, int unique) desc = (*mode == 'w') ? "put" : "append"; if (unique && stat(name, &st) == 0 && (name = gunique(name)) == NULL) { - logcmd(desc, -1, name, NULL, NULL, "cannot create unique file"); + logxfer(desc, -1, name, NULL, NULL, + "cannot create unique file"); goto cleanupstore; } @@ -1295,7 +1325,7 @@ store(const char *name, const char *mode, int unique) tdp = NULL; if (fout == NULL) { perror_reply(553, name); - logcmd(desc, -1, name, NULL, NULL, strerror(errno)); + logxfer(desc, -1, name, NULL, NULL, strerror(errno)); goto cleanupstore; } byte_count = -1; @@ -1343,7 +1373,7 @@ store(const char *name, const char *mode, int unique) timersub(&finish, &start, &td); tdp = &td; done: - logcmd(desc, byte_count, name, NULL, tdp, NULL); + logxfer(desc, byte_count, name, NULL, tdp, NULL); (*closefunc)(fout); cleanupstore: closedataconn(din); @@ -1883,7 +1913,10 @@ statcmd(void) su = NULL; } else if (pdata != -1) { reply(0, "in Passive mode"); - su = (struct sockinet *)&pasv_addr; + if (curclass.advertise.su_len != 0) + su = &curclass.advertise; + else + su = &pasv_addr; ispassive = 1; goto printaddr; } else if (usedefault == 0) { @@ -2030,6 +2063,16 @@ statcmd(void) CURCLASS_FLAGS_ISSET(sanenames) ? "en" : "dis"); reply(0, "PASV/LPSV/EPSV connections: %sabled", CURCLASS_FLAGS_ISSET(passive) ? "en" : "dis"); + if (curclass.advertise.su_len != 0) { + char buf[50]; /* big enough for IPv6 address */ + const char *bp; + + bp = inet_ntop(curclass.advertise.su_family, + (void *)&curclass.advertise.su_addr, + buf, sizeof(buf)); + if (bp != NULL) + reply(0, "PASV advertise address: %s", bp); + } if (curclass.portmin && curclass.portmax) reply(0, "PASV port range: %d - %d", curclass.portmin, curclass.portmax); @@ -2100,7 +2143,7 @@ reply(int n, const char *fmt, ...) } static void -dolog(struct sockinet *who) +logremotehost(struct sockinet *who) { if (getnameinfo((struct sockaddr *)&who->si_su, @@ -2117,8 +2160,7 @@ dolog(struct sockinet *who) } /* - * Record logout in wtmp file - * and exit with supplied status. + * Record logout in wtmp file and exit with supplied status. */ void dologout(int status) @@ -2241,7 +2283,10 @@ passive(void) pasv_addr.su_len = len; if (listen(pdata, 1) < 0) goto pasv_error; - a = (char *) &pasv_addr.su_addr; + if (curclass.advertise.su_len != 0) + a = (char *) &curclass.advertise.su_addr; + else + a = (char *) &pasv_addr.su_addr; p = (char *) &pasv_addr.su_port; #define UC(b) (((int) b) & 0xff) @@ -2373,9 +2418,15 @@ long_passive(char *cmd, int pf) #define UC(b) (((int) b) & 0xff) if (strcmp(cmd, "LPSV") == 0) { - switch (pasv_addr.su_family) { + struct sockinet *advert; + + if (curclass.advertise.su_len != 0) + advert = &curclass.advertise; + else + advert = &pasv_addr; + switch (advert->su_family) { case AF_INET: - a = (char *) &pasv_addr.su_addr; + a = (char *) &advert->su_addr; reply(228, "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), @@ -2383,7 +2434,7 @@ long_passive(char *cmd, int pf) return; #ifdef INET6 case AF_INET6: - a = (char *) &pasv_addr.su_6addr; + a = (char *) &advert->su_6addr; reply(228, "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 6, 16, @@ -2465,7 +2516,8 @@ extended_port(const char *arg) goto parsefail; if (sizeof(data_dest) < res->ai_addrlen) goto parsefail; - memcpy(&data_dest, res->ai_addr, res->ai_addrlen); + memcpy(&data_dest.si_su, res->ai_addr, res->ai_addrlen); + data_dest.su_len = res->ai_addrlen; #ifdef INET6 if (his_addr.su_family == AF_INET6 && data_dest.su_family == AF_INET6) { @@ -2580,7 +2632,6 @@ send_file_list(const char *whichf) int simple = 0; int freeglob = 0; glob_t gl; - off_t b; #ifdef __GNUC__ (void) &dout; @@ -2638,16 +2689,19 @@ send_file_list(const char *whichf) } if (S_ISREG(st.st_mode)) { + /* + * XXXRFC: + * should we follow RFC959 and not work + * for non directories? + */ if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } - b = fprintf(dout, "%s%s\n", dirname, + cprintf(dout, "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); - total_bytes += b; - total_bytes_out += b; byte_count += strlen(dirname) + 1; continue; } else if (!S_ISDIR(st.st_mode)) @@ -2672,7 +2726,12 @@ send_file_list(const char *whichf) * We have to do a stat to ensure it's * not a directory or special file. */ - /* XXX: follow RFC959 and filter out non files ? */ + /* + * XXXRFC: + * should we follow RFC959 and filter out + * non files ? lukem - NO!, or not until + * our ftp client uses MLS{T,D} for completion. + */ if (simple || (stat(nbuf, &st) == 0 && S_ISREG(st.st_mode))) { char *p; @@ -2687,10 +2746,8 @@ send_file_list(const char *whichf) p = nbuf; if (nbuf[0] == '.' && nbuf[1] == '/') p = &nbuf[2]; - b = fprintf(dout, "%s%s\n", p, + cprintf(dout, "%s%s\n", p, type == TYPE_A ? "\r" : ""); - total_bytes += b; - total_bytes_out += b; byte_count += strlen(nbuf) + 1; } } @@ -2729,48 +2786,102 @@ conffilename(const char *s) } /* - * logcmd -- - * based on the arguments, syslog a message: + * logxfer -- + * if logging > 1, then based on the arguments, syslog a message: * if bytes != -1 " = bytes" * else if file2 != NULL " " * else " " * if elapsed != NULL, append "in xxx.yyy seconds" * if error != NULL, append ": " + error + * + * if doxferlog != 0, syslog a wu-ftpd style xferlog entry */ void -logcmd(const char *command, off_t bytes, const char *file1, const char *file2, +logxfer(const char *command, off_t bytes, const char *file1, const char *file2, const struct timeval *elapsed, const char *error) { - char buf[MAXPATHLEN * 2 + 100], realfile[MAXPATHLEN]; - const char *p; - size_t len; + char buf[MAXPATHLEN * 2 + 100], realfile[MAXPATHLEN]; + const char *r1, *r2; + char direction; + size_t len; + time_t now; - if (logging <=1) + if (logging <=1 && !doxferlog) return; - if ((p = realpath(file1, realfile)) == NULL) - p = file1; - len = snprintf(buf, sizeof(buf), "%s %s", command, p); + r1 = r2 = NULL; + if ((r1 = realpath(file1, realfile)) == NULL) + r1 = file1; + if (file2 != NULL) + if ((r2 = realpath(file2, realfile)) == NULL) + r2 = file2; - if (bytes != (off_t)-1) { - len += snprintf(buf + len, sizeof(buf) - len, - " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes)); - } else if (file2 != NULL) { - if ((p = realpath(file2, realfile)) == NULL) - p = file2; - len += snprintf(buf + len, sizeof(buf) - len, " %s", p); + /* + * syslog command + */ + if (logging > 1) { + len = snprintf(buf, sizeof(buf), "%s %s", command, r1); + if (bytes != (off_t)-1) + len += snprintf(buf + len, sizeof(buf) - len, + " = " LLF " byte%s", (LLT) bytes, PLURAL(bytes)); + else if (r2 != NULL) + len += snprintf(buf + len, sizeof(buf) - len, + " %s", r2); + if (elapsed != NULL) + len += snprintf(buf + len, sizeof(buf) - len, + " in %ld.%.03d seconds", elapsed->tv_sec, + (int)(elapsed->tv_usec / 1000)); + if (error != NULL) + len += snprintf(buf + len, sizeof(buf) - len, + ": %s", error); + syslog(LOG_INFO, "%s", buf); } - if (elapsed != NULL) { - len += snprintf(buf + len, sizeof(buf) - len, - " in %ld.%.03d seconds", elapsed->tv_sec, - (int)(elapsed->tv_usec / 1000)); - } - if (error != NULL) - len += snprintf(buf + len, sizeof(buf) - len, ": %s", error); + /* + * syslog wu-ftpd style log entry, prefixed with "xferlog: " + */ + if (!doxferlog) + return; - syslog(LOG_INFO, "%s", buf); + if (strcmp(command, "get") == 0) + direction = 'o'; + else if (strcmp(command, "put") == 0 || strcmp(command, "append") == 0) + direction = 'i'; + else + return; + + time(&now); + syslog(LOG_INFO, + "xferlog%s: %.24s %ld %s " LLF " %s %c %s %c %c %s FTP 0 * %c", + +/* + * 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, + bytes == (off_t)-1 ? 0 : (LLT) bytes, + r1, + type == TYPE_A ? 'a' : 'b', + "_", /* XXX: take conversions into account? */ + direction, + + curclass.type == CLASS_GUEST ? 'a' : + curclass.type == CLASS_CHROOT ? 'g' : + curclass.type == CLASS_REAL ? 'r' : '?', + + curclass.type == CLASS_GUEST ? pw->pw_passwd : pw->pw_name, + error != NULL ? 'i' : 'c' + ); } /* diff --git a/libexec/ftpd/ftpd.conf.5 b/libexec/ftpd/ftpd.conf.5 index e4c91420d66a..0c7dc687bda9 100644 --- a/libexec/ftpd/ftpd.conf.5 +++ b/libexec/ftpd/ftpd.conf.5 @@ -1,4 +1,4 @@ -.\" $NetBSD: ftpd.conf.5,v 1.14 2000/11/16 13:15:14 lukem Exp $ +.\" $NetBSD: ftpd.conf.5,v 1.15 2000/12/18 02:32:51 lukem Exp $ .\" .\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -34,7 +34,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd November 16, 2000 +.Dd December 18, 2000 .Dt FTPD.CONF 5 .Os .Sh NAME @@ -118,12 +118,28 @@ command will return the class settings for the current user as defined by .Pp Each configuration line may be one of: .Bl -tag -width 4n +.It Sy advertise Ar class Ar host +Set the address to advertise in the response to the +.Sy PASV +and +.Sy LPSV +commands to the address for +.Ar host +(which may be either a host name or IP address). +This may be useful in some firewall configurations, although many +ftp clients may not work if the address being advertised is different +to the address that they've connected to. +If +.Ar class +is +.Dq none +or no argument is given, disable this. .It Sy checkportcmd Ar class Op Sy off Check the -PORT +.Sy PORT command for validity. The -PORT +.Sy PORT command will fail if the IP address specified does not match the .Tn FTP command connection, or if the remote TCP port number is less than @@ -172,15 +188,23 @@ A character. .El .Pp -The default root directory is -.Pa / -for -.Sy REAL -users, and the user's home directory for -.Sy GUEST -and -.Sy CHROOT -users. +The default root directory is: +.Bl -tag -width "CHROOT" -offset indent -compact +.It Sy CHROOT +The user's home directory. +.It Sy GUEST +If +.Fl a Ar anondir +is given, use +.Ar anondir , +otherwise the home directory of the +.Sq ftp +user. +.It Sy REAL +By default no +.Xr chroot 2 +is performed. +.El .It Sy classtype Ar class Ar type Set the class type of .Ar class @@ -247,26 +271,6 @@ Escape sequences are supported; refer to in .Xr ftpd 8 for more information. -.It Xo Sy limit Ar class -.Ar count Op Ar file -.Xc -Limit the maximum number of concurrent connections for -.Ar class -to -.Ar count , -with -.Sq 0 -meaning unlimited connections. -If the limit is exceeded and -.Ar file -is given, display its contents to the user. -Ignored if -.Ar class -is -.Dq none -or -.Ar count -is not specified. .It Sy homedir Ar class Op Sy pathformat If .Ar pathformat @@ -295,21 +299,52 @@ for and .Sy CHROOT users. +.It Xo Sy limit Ar class +.Ar count Op Ar file +.Xc +Limit the maximum number of concurrent connections for +.Ar class +to +.Ar count , +with +.Sq 0 +meaning unlimited connections. +If the limit is exceeded and +.Ar file +is given, display its contents to the user. +If +.Ar class +is +.Dq none +or +.Ar count +is not specified, disable this. +If +.Ar file +is a relative path, it will be searched for in +.Pa /etc +(which can be overridden with +.Fl c Ar confdir ) . .It Sy maxfilesize Ar class Ar size Set the maximum size of an uploaded file to .Ar size . +If +.Ar class +is +.Dq none +or no argument is given, disable this. .It Sy maxtimeout Ar class Ar time Set the maximum timeout period that a client may request, defaulting to two hours. This cannot be less than 30 seconds, or the value for .Sy timeout . -Ignored if +If .Ar class is .Dq none or .Ar time -is not specified. +is not specified, set to default of 2 hours. .It Sy modify Ar class Op Sy off If .Ar class @@ -318,7 +353,13 @@ is or .Sy off is given, disable the following commands: -CHMOD, DELE, MKD, RMD, RNFR, and UMASK. +.Sy CHMOD , +.Sy DELE , +.Sy MKD , +.Sy RMD , +.Sy RNFR , +and +.Sy UMASK . Otherwise, enable them. .It Sy motd Ar class Op Ar file If @@ -336,6 +377,12 @@ Escape sequences are supported; refer to in .Xr ftpd 8 for more information. +If +.Ar file +is a relative path, it will be searched for in +.Pa /etc +(which can be overridden with +.Fl c Ar confdir ) . .It Sy notify Ar class Op Ar fileglob If .Ar fileglob @@ -354,7 +401,12 @@ is .Dq none or .Sy off -is given, disallow passive (PASV/LPSV/EPSV) connections. +is given, disallow passive +.Sy ( PASV , +.Sy LPSV , +and +.Sy EPSV ) +connections. Otherwise, enable them. .It Sy portrange Ar class Ar min Ar max Set the range of port number which will be used for the passive data port. @@ -364,8 +416,15 @@ must be greater than and both numbers must be be between .Dv IPPORT_RESERVED (1024) and 65535. +If +.Ar class +is +.Dq none +or no arguments are given, disable this. .It Sy rateget Ar class Ar rate -Set the maximum get (RETR) transfer rate throttle for +Set the maximum get +.Pq Sy RETR +transfer rate throttle for .Ar class to .Ar rate @@ -373,28 +432,42 @@ bytes per second. If .Ar rate is 0, the throttle is disabled. +If +.Ar class +is +.Dq none +or no arguments are given, disable this. .Pp An optional suffix may be provided, which changes the intrepretation of .Ar rate as follows: .Bl -tag -width 3n -offset indent -compact .It b -Causes no modification. (Optional) +Causes no modification. (Default; optional) .It k Kilo; multiply the argument by 1024 .It m Mega; multiply the argument by 1048576 .It g Giga; multiply the argument by 1073741824 +.It t +Tera; multiply the argument by 1099511627776 .El .It Sy rateput Ar class Ar rate -Set the maximum put (STOR) transfer rate throttle for +Set the maximum put +.Pq Sy STOR +transfer rate throttle for .Ar class to .Ar rate bytes per second, which is parsed as per .Sy rateget Ar rate . +If +.Ar class +is +.Dq none +or no arguments are given, disable this. .It Sy sanenames Ar class Op Sy off If .Ar class @@ -432,23 +505,24 @@ Set the inactivity timeout period. (the default is fifteen minutes). This cannot be less than 30 seconds, or greater than the value for .Sy maxtimeout . -Ignored if +If .Ar class is .Dq none or .Ar time -is not specified. +is not specified, set to the default of 15 minutes. .It Sy umask Ar class Ar umaskval Set the umask to .Ar umaskval . -Ignored if +If .Ar class is .Dq none or .Ar umaskval -is not specified. +is not specified, set to the default of +.Li 027 . .It Sy upload Ar class Op Sy off If .Ar class @@ -457,9 +531,18 @@ is or .Sy off is given, disable the following commands: -APPE, STOR, and STOU, +.Sy APPE , +.Sy STOR , +and +.Sy STOU , as well as the modify commands: -CHMOD, DELE, MKD, RMD, RNFR, and UMASK. +.Sy CHMOD , +.Sy DELE , +.Sy MKD , +.Sy RMD , +.Sy RNFR , +and +.Sy UMASK . Otherwise, enable them. .El .Sh DEFAULTS diff --git a/libexec/ftpd/version.h b/libexec/ftpd/version.h index f545d73f4f56..78a5210a98f2 100644 --- a/libexec/ftpd/version.h +++ b/libexec/ftpd/version.h @@ -1,4 +1,4 @@ -/* $NetBSD: version.h,v 1.26 2000/12/04 10:50:39 itojun Exp $ */ +/* $NetBSD: version.h,v 1.27 2000/12/18 02:32:51 lukem Exp $ */ /*- * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. * All rights reserved. @@ -36,5 +36,5 @@ */ #ifndef FTPD_VERSION -#define FTPD_VERSION "NetBSD-ftpd 20001204" +#define FTPD_VERSION "NetBSD-ftpd 20001218" #endif