Add two new ftpd.conf(5) directives:

- 'denyquick'; deny a connection so tagged by ftpusers(5) after the USER
  command instead of the PASS command. whilst this might provide some
  info leakage of accounts names if you have some `real' or `chroot'
  users enabled and not others, it does prevent accidental entering of
  such passwords if you have all such users denied. This option is
  strongly recommended on anonymous-only servers.
  Functionality requested by Rob Windsor in [bin/12602]
- 'private'; don't display class related information in the output of STAT.
  For paranoid admins.
This commit is contained in:
lukem 2001-12-04 13:54:12 +00:00
parent c08523b03e
commit c31e16f75e
5 changed files with 171 additions and 85 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: conf.c,v 1.45 2001/12/01 10:25:29 lukem Exp $ */
/* $NetBSD: conf.c,v 1.46 2001/12/04 13:54:12 lukem Exp $ */
/*-
* Copyright (c) 1997-2001 The NetBSD Foundation, Inc.
@ -38,7 +38,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: conf.c,v 1.45 2001/12/01 10:25:29 lukem Exp $");
__RCSID("$NetBSD: conf.c,v 1.46 2001/12/04 13:54:12 lukem Exp $");
#endif /* not lint */
#include <sys/types.h>
@ -121,8 +121,10 @@ init_curclass(void)
curclass.umask = DEFAULT_UMASK;
CURCLASS_FLAGS_SET(checkportcmd);
CURCLASS_FLAGS_CLR(denyquick);
CURCLASS_FLAGS_SET(modify);
CURCLASS_FLAGS_SET(passive);
CURCLASS_FLAGS_CLR(private);
CURCLASS_FLAGS_CLR(sanenames);
CURCLASS_FLAGS_SET(upload);
}
@ -330,6 +332,9 @@ parse_conf(const char *findclass)
REASSIGN(conv->disable, disable);
REASSIGN(conv->command, convcmd);
} else if (strcasecmp(word, "denyquick") == 0) {
CONF_FLAG(denyquick);
} else if (strcasecmp(word, "display") == 0) {
CONF_STRING(display);
@ -446,6 +451,9 @@ parse_conf(const char *findclass)
curclass.portmin = minport;
curclass.portmax = maxport;
} else if (strcasecmp(word, "private") == 0) {
CONF_FLAG(private);
} else if (strcasecmp(word, "rateget") == 0) {
curclass.maxrateget = 0;
curclass.rateget = 0;

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.42 2001/07/13 05:37:49 lukem Exp $ */
/* $NetBSD: extern.h,v 1.43 2001/12/04 13:54:12 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -239,11 +239,13 @@ typedef enum {
typedef enum {
FLAG_checkportcmd = 1<<0, /* Check port commands */
FLAG_modify = 1<<1, /* Allow CHMOD, DELE, MKD, RMD, RNFR,
FLAG_denyquick = 1<<1, /* Check ftpusers(5) before PASS */
FLAG_modify = 1<<2, /* Allow CHMOD, DELE, MKD, RMD, RNFR,
UMASK */
FLAG_passive = 1<<2, /* Allow PASV mode */
FLAG_sanenames = 1<<3, /* Restrict names of uploaded files */
FLAG_upload = 1<<4 /* As per modify, but also allow
FLAG_passive = 1<<3, /* Allow PASV mode */
FLAG_private = 1<<4, /* Don't publish class info in STAT */
FLAG_sanenames = 1<<5, /* Restrict names of uploaded files */
FLAG_upload = 1<<6, /* As per modify, but also allow
APPE, STOR, STOU */
} classflag_t;

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpd.c,v 1.132 2001/12/01 10:25:30 lukem Exp $ */
/* $NetBSD: ftpd.c,v 1.133 2001/12/04 13:54:12 lukem Exp $ */
/*
* Copyright (c) 1997-2001 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.132 2001/12/01 10:25:30 lukem Exp $");
__RCSID("$NetBSD: ftpd.c,v 1.133 2001/12/04 13:54:12 lukem Exp $");
#endif
#endif /* not lint */
@ -561,7 +561,8 @@ sgetpwnam(const char *name)
}
static int login_attempts; /* number of failed login attempts */
static int askpasswd; /* had user command, ask for passwd */
static int askpasswd; /* had USER command, ask for PASSwd */
static int permitted; /* USER permitted */
static char curname[10]; /* current USER name */
/*
@ -578,6 +579,9 @@ static char curname[10]; /* current USER name */
void
user(const char *name)
{
char *class;
class = NULL;
if (logged_in) {
switch (curclass.type) {
case CLASS_GUEST:
@ -606,6 +610,9 @@ user(const char *name)
#endif
curclass.type = CLASS_REAL;
askpasswd = 0;
permitted = 0;
if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
/* need `pw' setup for checkaccess() and checkuser () */
if ((pw = sgetpwnam("ftp")) == NULL)
@ -618,34 +625,106 @@ user(const char *name)
reply(331,
"Guest login ok, type your name as password.");
}
if (!askpasswd && logging)
syslog(LOG_NOTICE,
"ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
return;
}
if (!askpasswd) {
if (logging)
syslog(LOG_NOTICE,
"ANONYMOUS FTP LOGIN REFUSED FROM %s",
remotehost);
end_login();
goto cleanup_user;
}
name = "ftp";
} else
pw = sgetpwnam(name);
pw = sgetpwnam(name);
if (logging)
strlcpy(curname, name, sizeof(curname));
/* check user in /etc/ftpusers, and setup class */
permitted = checkuser(_PATH_FTPUSERS, curname, 1, 0, &class);
/* check user in /etc/ftpchroot */
if (checkuser(_PATH_FTPCHROOT, curname, 0, 0, NULL)) {
if (curclass.type == CLASS_GUEST) {
syslog(LOG_NOTICE,
"Can't change guest user to chroot class; remove entry in %s",
_PATH_FTPCHROOT);
exit(1);
}
curclass.type = CLASS_CHROOT;
}
/* determine default class */
if (class == NULL) {
switch (curclass.type) {
case CLASS_GUEST:
class = xstrdup("guest");
break;
case CLASS_CHROOT:
class = xstrdup("chroot");
break;
case CLASS_REAL:
class = xstrdup("real");
break;
default:
syslog(LOG_ERR, "unknown curclass.type %d; aborting",
curclass.type);
abort();
}
}
/* parse ftpd.conf, setting up various parameters */
parse_conf(class);
/* if not guest user, check for valid shell */
if (pw == NULL)
permitted = 0;
else {
const char *cp, *shell;
if ((shell = pw->pw_shell) == NULL || *shell == 0)
shell = _PATH_BSHELL;
while ((cp = getusershell()) != NULL)
if (strcmp(cp, shell) == 0)
break;
endusershell();
if (cp == NULL && curclass.type != CLASS_GUEST)
permitted = 0;
}
/* deny quickly (after USER not PASS) if requested */
if (CURCLASS_FLAGS_ISSET(denyquick) && !permitted) {
reply(530, "User %s may not use FTP.", curname);
if (logging)
syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
remotehost, curname);
end_login();
goto cleanup_user;
}
/* if haven't asked yet (i.e, not anon), ask now */
if (!askpasswd) {
askpasswd = 1;
#ifdef SKEY
if (skey_haskey(name) == 0) {
const char *myskey;
if (skey_haskey(curname) == 0) {
const char *myskey;
myskey = skey_keyinfo(name);
reply(331, "Password [%s] required for %s.",
myskey ? myskey : "error getting challenge", name);
} else
myskey = skey_keyinfo(curname);
reply(331, "Password [%s] required for %s.",
myskey ? myskey : "error getting challenge",
curname);
} else
#endif
reply(331, "Password required for %s.", name);
reply(331, "Password required for %s.", curname);
}
askpasswd = 1;
cleanup_user:
/*
* Delay before reading passwd after first failed
* attempt to slow down passwd-guessing programs.
*/
if (login_attempts)
sleep((unsigned) login_attempts);
if (class)
free(class);
}
/*
@ -740,6 +819,8 @@ checkuser(const char *fname, const char *name, int def, int nofile,
gid_t *groups, *ng;
int gsize, i, found;
if (pw == NULL)
continue; /* no match for unknown user */
*p++ = '\0';
groups = NULL;
gsize = 16;
@ -823,6 +904,8 @@ end_login(void)
memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
pw = NULL;
logged_in = 0;
askpasswd = 0;
permitted = 0;
quietmessages = 0;
gidcount = 0;
curclass.type = CLASS_REAL;
@ -833,10 +916,8 @@ void
pass(const char *passwd)
{
int rval;
const char *cp, *shell;
char *class, root[MAXPATHLEN];
char root[MAXPATHLEN];
class = NULL;
if (logged_in || askpasswd == 0) {
reply(503, "Login with USER first.");
return;
@ -907,22 +988,8 @@ pass(const char *passwd)
}
}
/* password ok; see if anything else prevents login */
if (! checkuser(_PATH_FTPUSERS, pw->pw_name, 1, 0, &class)) {
reply(530, "User %s may not use FTP.", pw->pw_name);
if (logging)
syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
remotehost, pw->pw_name);
goto bad;
}
/* if not guest user, check for valid shell */
if ((shell = pw->pw_shell) == NULL || *shell == 0)
shell = _PATH_BSHELL;
while ((cp = getusershell()) != NULL)
if (strcmp(cp, shell) == 0)
break;
endusershell();
if (cp == NULL && curclass.type != CLASS_GUEST) {
/* password ok; check if anything else prevents login */
if (! permitted) {
reply(530, "User %s may not use FTP.", pw->pw_name);
if (logging)
syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
@ -955,36 +1022,6 @@ pass(const char *passwd)
logged_in = 1;
/* check user in /etc/ftpchroot */
if (checkuser(_PATH_FTPCHROOT, pw->pw_name, 0, 0, NULL)) {
if (curclass.type == CLASS_GUEST) {
syslog(LOG_NOTICE,
"Can't change guest user to chroot class; remove entry in %s",
_PATH_FTPCHROOT);
exit(1);
}
curclass.type = CLASS_CHROOT;
}
if (class == NULL) {
switch (curclass.type) {
case CLASS_GUEST:
class = xstrdup("guest");
break;
case CLASS_CHROOT:
class = xstrdup("chroot");
break;
case CLASS_REAL:
class = xstrdup("real");
break;
default:
syslog(LOG_ERR, "unknown curclass.type %d; aborting",
curclass.type);
abort();
}
}
/* parse ftpd.conf, setting up various parameters */
parse_conf(class);
connections = 1;
if (dopidfile)
count_users();
@ -1154,15 +1191,11 @@ pass(const char *passwd)
curclass.classname, CURCLASSTYPE);
}
(void) umask(curclass.umask);
goto cleanuppass;
return;
bad:
/* Forget all about it... */
end_login();
cleanuppass:
if (class)
free(class);
}
void
@ -2019,7 +2052,7 @@ statcmd(void)
(LLT)otb, PLURAL(otb),
(LLT)total_xfers, PLURAL(total_xfers));
if (logged_in) {
if (logged_in && !CURCLASS_FLAGS_ISSET(private)) {
struct ftpconv *cp;
reply(0, "%s", "");
@ -2043,6 +2076,8 @@ statcmd(void)
conffilename(curclass.limitfile));
if (! EMPTYSTR(curclass.chroot))
reply(0, "Chroot format: %s", curclass.chroot);
reply(0, "Deny bad ftpusers(5) quickly: : %sabled",
CURCLASS_FLAGS_ISSET(denyquick) ? "en" : "dis");
if (! EMPTYSTR(curclass.homedir))
reply(0, "Homedir format: %s", curclass.homedir);
if (curclass.maxfilesize == -1)

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ftpd.conf.5,v 1.17 2001/07/08 07:27:14 lukem Exp $
.\" $NetBSD: ftpd.conf.5,v 1.18 2001/12/04 13:54:13 lukem Exp $
.\"
.\" Copyright (c) 1997-2001 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 July 8, 2001
.Dd December 5, 2001
.Dt FTPD.CONF 5
.Os
.Sh NAME
@ -114,7 +114,10 @@ The
.Xr ftpd 8
.Sy STAT
command will return the class settings for the current user as defined by
.Nm "" .
.Nm "" ,
unless the
.Sy private
directive is set for the class.
.Pp
Each configuration line may be one of:
.Bl -tag -width 4n
@ -256,6 +259,33 @@ are replaced with the requested file (sans
.Pp
Conversion directives specified later in the file override earlier
conversions with the same suffix.
.It Sy denyquick Ar class Op Sy off
Enforce
.Xr ftpusers 5
rules after the
.Sy USER
command is received, rather than after the
.Sy PASS
command is received.
Whilst enabling this feature may allow information leakage about
available accounts (for example, if you allow some users of a
.Sy REAL
or
.Sy CHROOT
class but not others), it is useful in preventing a denied user
(such as
.Sq root )
from entering their password across an insecure connection.
This option is
.Em strongly
recommended for servers which run an anonymous-only service.
If
.Ar class
is
.Dq none
or
.Sy off
is given, disable this feature, otherwise enable it.
.It Sy display Ar class Op Ar file
If
.Ar file
@ -402,7 +432,7 @@ is
.Dq none
or
.Sy off
is given, disallow passive
is given, prevent passive
.Sy ( PASV ,
.Sy LPSV ,
and
@ -422,6 +452,17 @@ If
is
.Dq none
or no arguments are given, disable this.
.It Sy private Ar class Op Sy off
If
.Ar class
is
.Dq none
or
.Sy off
is given, do not display class information in the output of the
.Sy STAT
command.
Otherwise, display the information.
.It Sy rateget Ar class Ar rate
Set the maximum get
.Pq Sy RETR

View File

@ -1,4 +1,4 @@
/* $NetBSD: version.h,v 1.36 2001/12/01 10:25:30 lukem Exp $ */
/* $NetBSD: version.h,v 1.37 2001/12/04 13:54:13 lukem Exp $ */
/*-
* Copyright (c) 1999-2001 The NetBSD Foundation, Inc.
* All rights reserved.
@ -36,5 +36,5 @@
*/
#ifndef FTPD_VERSION
#define FTPD_VERSION "NetBSD-ftpd 20011201"
#define FTPD_VERSION "NetBSD-ftpd 20011205"
#endif