diff --git a/libexec/ftpd/conf.c b/libexec/ftpd/conf.c index d929aeaba17f..404ae124c68e 100644 --- a/libexec/ftpd/conf.c +++ b/libexec/ftpd/conf.c @@ -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 #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 @@ -47,7 +47,9 @@ __RCSID("$NetBSD: conf.c,v 1.24 1999/12/12 14:05:54 lukem Exp $"); #include #include +#include #include +#include #include #include #include @@ -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 : ""); + 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 : ""); 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); +} diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index a4f57e3db2d5..434dfdffd632 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -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; diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 22b7c8702cd3..49d74ff2803e 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -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" diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index caed9405eaa4..c145a5ba6e49 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -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; diff --git a/libexec/ftpd/ftpd.conf.5 b/libexec/ftpd/ftpd.conf.5 index 526e31da2921..b5dbcfaf8871 100644 --- a/libexec/ftpd/ftpd.conf.5 +++ b/libexec/ftpd/ftpd.conf.5 @@ -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 diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h index 243c3815d9b8..38b96ae06947 100644 --- a/libexec/ftpd/pathnames.h +++ b/libexec/ftpd/pathnames.h @@ -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" diff --git a/libexec/ftpd/version.h b/libexec/ftpd/version.h index 651a67fedde3..bf924ae325f6 100644 --- a/libexec/ftpd/version.h +++ b/libexec/ftpd/version.h @@ -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