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:
parent
c08523b03e
commit
c31e16f75e
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user