features:

* add connection limits (`limit' keyword in ftpd.conf)
* move initialisation of curclass from parse_conf() to new function
  init_curclass()
* implement count_users(), which determines the number of users in a given
  class. a file - /var/run/ftpd.pids-<class> - is used to store a list
  of pids in use (effectively an array of pid_t's), and its size is reduced
  as necessary.
* new % modifiers in format_file:
	%c	class
	%M	maximum connection count
	%N	current connection count
* always end_login()s, even for refused connections

bugs fixed:
* remove \n from %T output
* fix some inconsistencies in the man pages
* ensure that both `ftp' *and* `anonymous' are allowed in ftpusers.
  (this was accidently broken in a recent commit to be ``or'' not ``and'')
* use MAXPATHLEN not MAXPATHLEN+1
* crank copyright date on modified files
* crank version
This commit is contained in:
lukem 2000-01-08 11:09:56 +00:00
parent e81e75d36b
commit 16e886121d
7 changed files with 265 additions and 74 deletions

View File

@ -1,7 +1,7 @@
/* $NetBSD: conf.c,v 1.24 1999/12/12 14:05:54 lukem Exp $ */
/* $NetBSD: conf.c,v 1.25 2000/01/08 11:09:56 lukem Exp $ */
/*-
* Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -38,7 +38,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: conf.c,v 1.24 1999/12/12 14:05:54 lukem Exp $");
__RCSID("$NetBSD: conf.c,v 1.25 2000/01/08 11:09:56 lukem Exp $");
#endif /* not lint */
#include <sys/types.h>
@ -47,7 +47,9 @@ __RCSID("$NetBSD: conf.c,v 1.24 1999/12/12 14:05:54 lukem Exp $");
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -70,6 +72,45 @@ static int filetypematch __P((char *, int));
struct ftpclass curclass;
/*
* Initialise curclass to an `empty' state
*/
void
init_curclass()
{
struct ftpconv *conv, *cnext;
for (conv = curclass.conversions; conv != NULL; conv = cnext) {
REASSIGN(conv->suffix, NULL);
REASSIGN(conv->types, NULL);
REASSIGN(conv->disable, NULL);
REASSIGN(conv->command, NULL);
cnext = conv->next;
free(conv);
}
curclass.checkportcmd = 0;
REASSIGN(curclass.classname, NULL);
curclass.conversions = NULL;
REASSIGN(curclass.display, NULL);
curclass.limit = -1; /* unlimited connections */
REASSIGN(curclass.limitfile, NULL);
curclass.maxrateget = 0;
curclass.maxrateput = 0;
curclass.maxtimeout = 7200; /* 2 hours */
curclass.modify = 1;
REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG));
REASSIGN(curclass.notify, NULL);
curclass.passive = 1;
curclass.maxrateget = 0;
curclass.maxrateput = 0;
curclass.rateget = 0;
curclass.rateput = 0;
curclass.timeout = 900; /* 15 minutes */
curclass.umask = 027;
curclass.upload = 1;
}
/*
* Parse the configuration file, looking for the named class, and
* define curclass to contain the appropriate settings.
@ -89,33 +130,8 @@ parse_conf(findclass)
unsigned int timeout;
struct ftpconv *conv, *cnext;
init_curclass();
REASSIGN(curclass.classname, xstrdup(findclass));
for (conv = curclass.conversions; conv != NULL; conv = cnext) {
REASSIGN(conv->suffix, NULL);
REASSIGN(conv->types, NULL);
REASSIGN(conv->disable, NULL);
REASSIGN(conv->command, NULL);
cnext = conv->next;
free(conv);
}
curclass.checkportcmd = 0;
curclass.conversions = NULL;
REASSIGN(curclass.display, NULL);
curclass.maxrateget = 0;
curclass.maxrateput = 0;
curclass.maxtimeout = 7200; /* 2 hours */
curclass.modify = 1;
REASSIGN(curclass.motd, xstrdup(_PATH_FTPLOGINMESG));
REASSIGN(curclass.notify, NULL);
curclass.passive = 1;
curclass.maxrateget = 0;
curclass.maxrateput = 0;
curclass.rateget = 0;
curclass.rateput = 0;
curclass.timeout = 900; /* 15 minutes */
curclass.umask = 027;
curclass.upload = 1;
if (strcasecmp(findclass, "guest") == 0) {
curclass.modify = 0;
curclass.umask = 0707;
@ -232,6 +248,21 @@ parse_conf(findclass)
arg = xstrdup(arg);
REASSIGN(curclass.display, arg);
} else if (strcasecmp(word, "limit") == 0) {
int limit;
if (none || EMPTYSTR(arg))
continue;
limit = (int)strtol(arg, &endp, 10);
if (*endp != 0) {
syslog(LOG_WARNING,
"%s line %d: invalid limit %s",
infile, (int)line, arg);
continue;
}
curclass.limit = limit;
REASSIGN(curclass.limitfile, xstrdup(p));
} else if (strcasecmp(word, "maxtimeout") == 0) {
if (none || EMPTYSTR(arg))
continue;
@ -385,7 +416,7 @@ show_chdir_messages(code)
glob_t gl;
time_t now, then;
int age;
char cwd[MAXPATHLEN + 1];
char cwd[MAXPATHLEN];
char *cp, **rlist;
/* Setup list for directory cache */
@ -470,6 +501,13 @@ format_file(file, code)
if (*p == '%') {
p++;
switch (*p) {
case 'c':
b += printf("%s",
curclass.classname ?
curclass.classname : "<unknown>");
break;
case 'C':
if (getcwd(cwd, sizeof(cwd)-1) == NULL){
syslog(LOG_WARNING,
@ -479,26 +517,46 @@ format_file(file, code)
}
b += printf("%s", cwd);
break;
case 'E':
/* XXXX email address */
break;
case 'L':
b += printf("%s", hostname);
break;
case 'M':
if (curclass.limit == -1)
b += printf("unlimited");
else
b += printf("%d",
curclass.limit);
break;
case 'N':
if (connections > 0)
b += printf("%d", connections);
break;
case 'R':
b += printf("%s", remotehost);
break;
case 'T':
now = time(NULL);
b += printf("%.25s", ctime(&now));
b += printf("%.24s", ctime(&now));
break;
case 'U':
b += printf("%s",
pw ? pw->pw_name : "<unknown>");
break;
case '%':
PUTC('%');
break;
}
} else {
PUTC(*p);
@ -524,7 +582,7 @@ strend(s1, s2)
const char *s1;
char *s2;
{
static char buf[MAXPATHLEN + 1];
static char buf[MAXPATHLEN];
char *start;
size_t l1, l2;
@ -683,3 +741,61 @@ strsuftoi(arg)
return (val);
}
void
count_users()
{
char fn[MAXPATHLEN];
int fd, i, last;
size_t count;
pid_t *pids, mypid;
struct stat sb;
(void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn));
(void)strlcat(fn, curclass.classname, sizeof(fn));
pids = NULL;
connections = 1;
if ((fd = open(fn, O_RDWR | O_CREAT | O_EXLOCK, 0600)) == -1)
return;
if (fstat(fd, &sb) == -1)
goto cleanup_count;
if ((pids = malloc(sb.st_size + sizeof(pid_t))) == NULL)
goto cleanup_count;
count = read(fd, pids, sb.st_size);
if (count < 0 || count != sb.st_size)
goto cleanup_count;
count /= sizeof(pid_t);
mypid = getpid();
last = 0;
for (i = 0; i < count; i++) {
if (pids[i] == 0)
continue;
if (kill(pids[i], 0) == -1 && errno != EPERM) {
if (mypid != 0) {
pids[i] = mypid;
mypid = 0;
last = i;
}
} else {
connections++;
last = i;
}
}
if (mypid != 0) {
if (pids[last] != 0)
last++;
pids[last] = mypid;
}
count = (last + 1) * sizeof(pid_t);
if (lseek(fd, 0, SEEK_SET) == -1)
goto cleanup_count;
if (write(fd, pids, count) == -1)
goto cleanup_count;
(void)ftruncate(fd, count);
cleanup_count:
(void)flock(fd, LOCK_UN);
close(fd);
REASSIGN(pids, NULL);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.21 1999/12/12 14:05:54 lukem Exp $ */
/* $NetBSD: extern.h,v 1.22 2000/01/08 11:09:56 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -36,7 +36,7 @@
*/
/*-
* Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -103,6 +103,7 @@
void blkfree __P((char **));
char *conffilename __P((const char *));
char **copyblk __P((char **));
void count_users __P((void));
void cwd __P((const char *));
void delete __P((const char *));
char **do_conversion __P((const char *));
@ -112,6 +113,7 @@ int format_file __P((const char *, int));
int ftpd_pclose __P((FILE *));
FILE *ftpd_popen __P((char *[], const char *, int));
char *getline __P((char *, int, FILE *));
void init_curclass __P((void));
void logcmd __P((const char *, off_t, const char *, const char *,
const struct timeval *, const char *));
void logwtmp __P((const char *, const char *, const char *));
@ -160,6 +162,8 @@ struct ftpclass {
char *classname; /* Current class */
struct ftpconv *conversions; /* List of conversions */
char *display; /* Files to display upon chdir */
int limit; /* Max connections (-1 = unlimited) */
char *limitfile; /* File to display if limit reached */
int maxrateget; /* Maximum get transfer rate throttle */
int maxrateput; /* Maximum put transfer rate throttle */
unsigned int maxtimeout; /* Maximum permitted timeout */
@ -195,6 +199,7 @@ union sockunion {
extern int yyparse __P((void));
extern char cbuf[];
extern int connections;
extern struct ftpclass curclass;
extern union sockunion data_dest;
extern int debug;

View File

@ -1,6 +1,6 @@
.\" $NetBSD: ftpd.8,v 1.48 1999/12/19 00:09:31 lukem Exp $
.\" $NetBSD: ftpd.8,v 1.49 2000/01/08 11:09:56 lukem Exp $
.\"
.\" Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
@ -67,7 +67,7 @@
.\"
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
.\"
.Dd December 19, 1999
.Dd January 8, 2000
.Dt FTPD 8
.Os
.Sh NAME
@ -364,10 +364,19 @@ The supported escape strings are:
.Bl -tag -width "Escape" -offset indent -compact
.It Sy "Escape"
.Sy Description
.It "\&%c"
Class name.
.It "\&%C"
Current working directory.
.It "\&%L"
Local hostname.
.It "\&%M"
Maximum number of users for this class.
Displays
.Dq unlimited
if there's no limit.
.It "\&%N"
Current number of users for this class.
.It "\&%R"
Remote hostname.
.It "\&%T"

View File

@ -1,7 +1,7 @@
/* $NetBSD: ftpd.c,v 1.81 1999/12/21 12:56:15 lukem Exp $ */
/* $NetBSD: ftpd.c,v 1.82 2000/01/08 11:09:56 lukem Exp $ */
/*
* Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -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.81 1999/12/21 12:56:15 lukem Exp $");
__RCSID("$NetBSD: ftpd.c,v 1.82 2000/01/08 11:09:56 lukem Exp $");
#endif
#endif /* not lint */
@ -181,6 +181,7 @@ union sockunion pasv_addr;
int data;
jmp_buf errcatch, urgcatch;
int logged_in;
int connections = 1;
struct passwd *pw;
int debug;
int sflag;
@ -459,6 +460,10 @@ main(argc, argv)
}
#endif /* KERBEROS5 */
init_curclass();
curclass.timeout = 300; /* 5 minutes, as per login(1) */
curclass.type = CLASS_REAL;
/* If logins are disabled, print out the message. */
if (format_file(_PATH_NOLOGIN, 530)) {
reply(530, "System not available.");
@ -468,9 +473,6 @@ main(argc, argv)
/* reply(220,) must follow */
reply(220, "%s FTP server (%s) ready.", hostname, version);
curclass.timeout = 300; /* 5 minutes, as per login(1) */
curclass.type = CLASS_REAL;
(void) setjmp(errcatch);
for (;;)
(void) yyparse();
@ -562,7 +564,7 @@ user(name)
/* need `pw' setup for checkaccess() and checkuser () */
if ((pw = sgetpwnam("ftp")) == NULL)
reply(530, "User %s unknown.", name);
else if (! checkaccess("ftp") && ! checkaccess("anonymous"))
else if (! checkaccess("ftp") || ! checkaccess("anonymous"))
reply(530, "User %s access denied.", name);
else {
curclass.type = CLASS_GUEST;
@ -859,8 +861,7 @@ skip:
if (logging)
syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
remotehost, pw->pw_name);
pw = (struct passwd *) NULL;
goto cleanuppass;
goto bad;
}
/* check for valid shell, if not guest user */
if ((shell = pw->pw_shell) == NULL || *shell == 0)
@ -874,14 +875,13 @@ skip:
if (logging)
syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s",
remotehost, pw->pw_name);
pw = (struct passwd *) NULL;
goto cleanuppass;
goto bad;
}
login_attempts = 0; /* this time successful */
if (setegid((gid_t)pw->pw_gid) < 0) {
reply(550, "Can't set gid.");
goto cleanuppass;
goto bad;
}
(void) initgroups(pw->pw_name, pw->pw_gid);
@ -928,6 +928,18 @@ skip:
/* parse ftpd.conf, setting up various parameters */
parse_conf(class);
count_users();
if (curclass.limit != -1 && connections > curclass.limit) {
if (! EMPTYSTR(curclass.limitfile))
(void)format_file(conffilename(curclass.limitfile),530);
reply(530,
"User %s access denied, connection limit of %d reached",
pw->pw_name, curclass.limit);
syslog(LOG_NOTICE,
"Maximum connection limit of %d for class %s reached, login refused",
curclass.limit, curclass.classname);
goto bad;
}
home = "/";
switch (curclass.type) {
@ -1042,7 +1054,7 @@ retrieve(argv, name)
}
}
if (argv != NULL) {
char temp[MAXPATHLEN + 1];
char temp[MAXPATHLEN];
if (strcmp(argv[0], INTERNAL_LS) == 0) {
isls = 1;
@ -1904,6 +1916,14 @@ epsvonly:;
lreply(0, "Notify fileglob: %s", curclass.notify);
lreply(0, "Idle timeout: %d, maximum timeout: %d",
curclass.timeout, curclass.maxtimeout);
lreply(0, "Current connections: %d", connections);
if (curclass.limit == -1)
lreply(0, "Maximum connections: unlimited");
else
lreply(0, "Maximum connections: %d", curclass.limit);
if (curclass.limitfile)
lreply(0, "Connection limit exceeded file: %s",
curclass.limitfile);
if (curclass.motd != NULL)
lreply(0, "MotD file: %s", curclass.motd);
lreply(0,
@ -2054,7 +2074,7 @@ static void
replydirname(name, message)
const char *name, *message;
{
char npath[MAXPATHLEN + 1];
char npath[MAXPATHLEN];
int i;
for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
@ -2097,7 +2117,7 @@ removedir(name)
void
pwd()
{
char path[MAXPATHLEN + 1];
char path[MAXPATHLEN];
if (getcwd(path, sizeof(path) - 1) == NULL)
reply(550, "Can't get the current directory: %s.",
@ -2368,7 +2388,7 @@ static char *
gunique(local)
const char *local;
{
static char new[MAXPATHLEN + 1];
static char new[MAXPATHLEN];
struct stat st;
char *cp;
int count;
@ -2509,7 +2529,7 @@ send_file_list(whichf)
continue;
while ((dir = readdir(dirp)) != NULL) {
char nbuf[MAXPATHLEN + 1];
char nbuf[MAXPATHLEN];
if (dir->d_name[0] == '.' && dir->d_namlen == 1)
continue;
@ -2574,7 +2594,7 @@ char *
conffilename(s)
const char *s;
{
static char filename[MAXPATHLEN + 1];
static char filename[MAXPATHLEN];
if (*s == '/')
strlcpy(filename, s, sizeof(filename));
@ -2602,7 +2622,7 @@ logcmd(command, bytes, file1, file2, elapsed, error)
const struct timeval *elapsed;
const char *error;
{
char buf[MAXPATHLEN * 2 + 100], realfile[MAXPATHLEN + 1];
char buf[MAXPATHLEN * 2 + 100], realfile[MAXPATHLEN];
const char *p;
size_t len;

View File

@ -1,6 +1,6 @@
.\" $NetBSD: ftpd.conf.5,v 1.5 1999/12/26 09:42:18 lukem Exp $
.\" $NetBSD: ftpd.conf.5,v 1.6 2000/01/08 11:09:56 lukem Exp $
.\"
.\" Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
@ -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 December 26, 1999
.Dd January 8, 2000
.Dt FTPD.CONF 5
.Os
.Sh NAME
@ -109,7 +109,9 @@ encouraged that this option be used, espcially for sites concerned
with potential security problems with
.Tn FTP
bounce attacks.
If class is
If
.Ar class
is
.Dq none
or
.Sy off
@ -201,18 +203,42 @@ 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 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 class is
Ignored if
.Ar class
is
.Dq none
or
.Ar time
is not specified.
.It Sy modify Ar class Op Sy off
If class is
If
.Ar class
is
.Dq none
or
.Sy off
@ -247,14 +273,18 @@ Otherwise, each time the user enters a new directory,
notify the user of any files matching
.Ar fileglob .
.It Sy passive Ar class Op Sy off
If class is
If
.Ar class
is
.Dq none
or
.Sy off
is given, disallow passive (PASV/LPSV/EPSV) connections.
Otherwise, enable them.
.It Sy rateget Ar rate
Set the maximum get (RETR) transfer rate throttle to
.It Sy rateget Ar class Ar rate
Set the maximum get (RETR) transfer rate throttle for
.Ar class
to
.Ar rate .
If
.Ar rate
@ -273,8 +303,10 @@ Mega; multiply the argument by 1048576
.It g
Giga; multiply the argument by 1073741824
.El
.It Sy rateput Ar class
Set the maximum put (STOR) transfer rate throttle to
.It Sy rateput Ar class Ar rate
Set the maximum put (STOR) transfer rate throttle for
.Ar class
to
.Ar rate ,
which is parsed as per
.Sy rateget Ar rate .
@ -283,7 +315,9 @@ 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 class is
Ignored if
.Ar class
is
.Dq none
or
.Ar time
@ -291,13 +325,17 @@ is not specified.
.It Sy umask Ar class Ar umaskval
Set the umask to
.Ar umaskval .
Ignored if class is
Ignored if
.Ar class
is
.Dq none
or
.Ar umaskval
is not specified.
.It Sy upload Ar class Op Sy off
If class is
If
.Ar class
is
.Dq none
or
.Sy off

View File

@ -1,4 +1,4 @@
/* $NetBSD: pathnames.h,v 1.8 1998/06/08 07:13:13 lukem Exp $ */
/* $NetBSD: pathnames.h,v 1.9 2000/01/08 11:09:56 lukem Exp $ */
/*
* Copyright (c) 1989, 1993
@ -44,4 +44,7 @@
#define _PATH_FTPLOGINMESG "motd"
#define _PATH_FTPUSERS "ftpusers"
#define _PATH_FTPWELCOME "ftpwelcome"
#define _PATH_CLASSPIDS "/var/run/ftpd.pids-"
#define TMPFILE "/tmp/ftpdXXXXXXX"

View File

@ -1,6 +1,6 @@
/* $NetBSD: version.h,v 1.3 1999/12/21 12:57:45 lukem Exp $ */
/* $NetBSD: version.h,v 1.4 2000/01/08 11:09:56 lukem Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -36,5 +36,5 @@
*/
#ifndef FTPD_VERSION
#define FTPD_VERSION "Version: NetBSD-ftpd/19991221"
#define FTPD_VERSION "Version: NetBSD-ftpd/20000108"
#endif