From 5015048190e303b605ceb52f74a3eb06de9b48bf Mon Sep 17 00:00:00 2001 From: lukem Date: Mon, 18 Dec 2000 02:32:50 +0000 Subject: [PATCH] Features: * Add ftpd.conf(5) directive `advertise'; change the address that is advertised to the client for PASV transfers. this may be useful in certain firewall/NAT environments. Feature requested in [bin/9606] by Scott Presnell. * Add -X option; syslog wu-ftpd style xferlog messages, prefixed with `xferlog: '. An example line from syslog (wrapped): Dec 16 18:50:24 odysseus ftpd[571]: xferlog: Sat Dec 16 18:50:24 2000 2 localhost 3747328 /pub/WLW2K601.EXE b _ o a lukem@ FTP 0 * c These messages can be converted to a wu-ftpd style xferlog file suitable for parsing with third-party tools with something like: grep 'xferlog: ' /var/log/xferlog | \ sed -e 's/^.*xferlog: //' >wuxferlog The format is the same as the wu-ftpd xferlog entries (with the leading syslog stuff), but different from the wu-ftpd syslogged xferlog entries because the latter is not as easy to convert into the standard xferlog file format. The choice to only syslog the xferlog messages rather than append to a /var/log/xferlog file was made because the latter doesn't work to well in the situation where the logfile is rotated and compressed and a long-running ftpd still has a file-descriptor to the now nonexistant xferlog file, and the log message will then get lost. Feature requested in [bin/11651] by Hubert Feyrer. Fixes: * In ftpd(8), clarify the -a and -c options. * More clarifications in ftpd.conf(5). * Ensure that all ftpd.conf commands set a parameter back to sane defaults if an argument of `none' or bad settings are given. * Support the `chroot' directive for `REAL' users too (for consistency). * For `GUEST' users, store the supplied password in pw->pw_passwd for use later in the xferlog. * If show_chdir_messages() is given a code of -1, flush the cache of visited directories. Invoke show_chdir_messages(-1) in end_login(). * Only syslog session stats if logging is requested. * Rename logcmd() -> logxfer(), and dolog() -> logremotehost(). * Use cprintf() instead of fprintf() where appropriate. * Minor KNF, and make a couple of functions static that were declared static. --- libexec/ftpd/cmds.c | 24 ++-- libexec/ftpd/conf.c | 135 +++++++++++++++++----- libexec/ftpd/extern.h | 69 ++++++------ libexec/ftpd/ftpcmd.y | 10 +- libexec/ftpd/ftpd.8 | 48 ++++++-- libexec/ftpd/ftpd.c | 235 ++++++++++++++++++++++++++++----------- libexec/ftpd/ftpd.conf.5 | 175 +++++++++++++++++++++-------- libexec/ftpd/version.h | 4 +- 8 files changed, 503 insertions(+), 197 deletions(-) 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