diff -ru wu-ftpd-2.6.2/README.AUTOCONF wu-ftpd-2.6.2hl/README.AUTOCONF --- wu-ftpd-2.6.2/README.AUTOCONF 1999-10-01 17:04:13.000000000 +0200 +++ wu-ftpd-2.6.2hl/README.AUTOCONF 2002-03-28 02:09:44.000000000 +0100 @@ -51,6 +51,8 @@ --disable-throughput Don't keep track of user's throughput --disable-count Don't keep track of transferred bytes (for statistics) +--disable-hostlimit Disables support for the host-limit keyword in + the ftpaccess file --disable-newlines Suppress some extra blank lines --enable-crackers Don't wait for password entry if someone tries to log in with a wrong username. --- wu-ftpd-2.6.2/config.h.in 2001-11-29 18:10:57.000000000 +0100 +++ wu-ftpd-2.6.2hl/config.h.in 2002-03-28 02:09:44.000000000 +0100 @@ -211,6 +211,13 @@ #undef TRANSFER_LIMIT /* + * HOST_LIMIT + * Limit number of simultaneous connections from a given host IP address. + */ + +#undef HOST_LIMIT + +/* * NO_SUCKING_NEWLINES * Don't suppress some extra blank lines on messages and banners. */ diff -ru wu-ftpd-2.6.2/config.h.noac wu-ftpd-2.6.2hl/config.h.noac --- wu-ftpd-2.6.2/config.h.noac 2001-11-29 18:10:58.000000000 +0100 +++ wu-ftpd-2.6.2hl/config.h.noac 2002-03-28 02:09:44.000000000 +0100 @@ -205,6 +205,13 @@ #endif /* + * HOST_LIMIT + * Limit number of simultaneous connections from a given host IP address. + */ + +#define HOST_LIMIT + +/* * NO_SUCKING_NEWLINES * Don't suppress some extra blank lines on messages and banners. */ --- wu-ftpd-2.6.2hl/configure.in 2001-11-29 18:10:58.000000000 +0100 +++ wu-ftpd-2.6.2hl-lf/configure.in 2004-05-04 16:36:55.349155000 +0200 @@ -162,6 +162,8 @@ [ throughput=$enableval ], [ throughput=yes ]) AC_ARG_ENABLE(count, [ --disable-count don't keep track of bytes for statistics], [ count=$enableval ], [ count=yes ]) +AC_ARG_ENABLE(hostlimit, [ --disable-hostlimit disable the HOST-LIMIT keyword in ftpaccess + [ hostlimit=$enableval ], [ hostlimit=yes ]) AC_ARG_ENABLE(newlines, [ --disable-newlines suppress some extra blank lines], [ newlines=$enableval ], [ newlines=yes ]) AC_ARG_ENABLE(crackers, [ --enable-crackers don't wait for password if the username doesn't exist], @@ -900,6 +902,9 @@ AC_DEFINE(TRANSFER_COUNT) AC_DEFINE(TRANSFER_LIMIT) fi +if test $hostlimit = yes; then + AC_DEFINE(HOST_LIMIT) +fi if test $newlines = no; then AC_DEFINE(NO_SUCKING_NEWLINES) fi diff -ru wu-ftpd-2.6.2/doc/ftpaccess.5 wu-ftpd-2.6.2hl/doc/ftpaccess.5 --- wu-ftpd-2.6.2/doc/ftpaccess.5 2000-07-01 19:49:09.000000000 +0200 +++ wu-ftpd-2.6.2hl/doc/ftpaccess.5 2002-03-28 02:09:44.000000000 +0100 @@ -256,6 +256,17 @@ UUCP L.sys file. .TP 0.5i +.B host-limit + +Limit to simultaneous connections per host IP address at +times , displaying if the user is denied access. +Limit check is performed at login time only. +If multiple "host-limit" commands can apply to the current session, the first +applicable one is used. Failing to define a valid limit, or a limit of -1, +is equivalent to unlimited. is in same format as the times in the +UUCP L.sys file. + +.TP 0.5i .B noretrieve [absolute|relative] [class=] ... [-] ... Always deny retrieve-ability of these files. If the files are a path diff -ru wu-ftpd-2.6.2/src/access.c wu-ftpd-2.6.2hl/src/access.c --- wu-ftpd-2.6.2/src/access.c 2000-07-01 20:17:38.000000000 +0200 +++ wu-ftpd-2.6.2hl/src/access.c 2002-03-28 02:09:44.000000000 +0100 @@ -88,6 +88,9 @@ #define MAXLINE 80 static char incline[MAXLINE]; int pidfd = -1; +#ifdef HOST_LIMIT +int ripfd = -1; +#endif extern int Bypass_PID_Files; #ifndef HELP_CRACKERS @@ -786,6 +789,39 @@ return (-1); } +#ifdef HOST_LIMIT +/*************************************************************************/ +/* FUNCTION : acl_gethostlimit */ +/* PURPOSE : Scan the ACL buffer and determine what limit applies to */ +/* the user */ +/* ARGUMENTS : pointer class name, pointer to ACL buffer */ +/*************************************************************************/ + +int acl_gethostlimit(char *class, char *msgpathbuf) +{ + int limit; + struct aclmember *entry = NULL; + + if (msgpathbuf) + *msgpathbuf = '\0'; + + /* host-limit [] */ + while (getaclentry("host-limit", &entry)) { + if (!ARG0 || !ARG1 || !ARG2) + continue; + if (!strcasecmp(class, ARG0)) { + limit = atoi(ARG1); + if (validtime(ARG2)) { + if (ARG3 && msgpathbuf) + strcpy(msgpathbuf, ARG3); + return (limit); + } + } + } + return (-1); +} +#endif + /*************************************************************************/ /* FUNCTION : acl_getnice */ /* PURPOSE : Scan the ACL buffer and determine what nice value applies */ @@ -1200,6 +1236,231 @@ return (count); } +#ifdef HOST_LIMIT +/*************************************************************************/ +/* FUNCTION : acl_counthosts */ +/* PURPOSE : Check the anonymous FTP access lists to see if this */ +/* access is permitted. */ +/* ARGUMENTS : none */ +/*************************************************************************/ + +int acl_counthosts(char *class) +{ + int count, + which; + char ripfile[MAXPATHLEN]; + char buf[4*MAXUSERS]; + int r[4]; +#ifndef HAVE_FLOCK +struct flock arg; +#endif + + if (Bypass_PID_Files) + return (0); + /* + * if ripfd was not opened previously... + * ripfd must stay open after the chroot(~ftp) + */ + + sprintf(ripfile, _PATH_RIPNAMES, class); + + if (ripfd < 0) { + mode_t oldmask; + oldmask = umask(0); + ripfd = open(ripfile, O_RDWR | O_CREAT, 0644); + (void) umask(oldmask); + } + + if (ripfd < 0) { + syslog(LOG_ERR, "cannot open pid file %s: %m", ripfile); + return -1; + } + +#ifdef HAVE_FLOCK + while (flock(ripfd, LOCK_EX)) { +#ifndef NO_PID_SLEEP_MSGS + syslog(LOG_ERR, "sleeping: flock of pid file failed: %m"); +#endif +#else + arg.l_type = F_WRLCK; + arg.l_whence = arg.l_start = arg.l_len = 0; + while ( -1 == fcntl( ripfd, F_SETLK, &arg) ) { +#ifndef NO_PID_SLEEP_MSGS + syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %m"); +#endif +#endif + sleep(1); + } + lseek(ripfd, (off_t)0, SEEK_SET); + + sscanf(remoteaddr,"%d.%d.%d.%d",r,r+1,r+2,r+3); + count = 0; + + if (read(ripfd, (void *)buf, sizeof(buf)) == sizeof(buf)) { + for (which = 0; which < 4*MAXUSERS; which+=4) + if ((buf[which] & 0xFF) == r[0] + && (buf[which+1] & 0xFF) == r[1] + && (buf[which+2] & 0xFF) == r[2] + && (buf[which+3] & 0xFF) == r[3]) + count++; + } +#ifdef HAVE_FLOCK + flock(ripfd, LOCK_UN); +#else + arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0; + fcntl(ripfd, F_SETLK, &arg); +#endif + return (count); +} + +/*************************************************************************/ +/* FUNCTION : acl_addhost */ +/* PURPOSE : Add the current host IP to the list of addresses in the */ +/* specified class. */ +/* ARGUMENTS : The name of the class to join, the "slot" number */ +/*************************************************************************/ + +void acl_addhost(char *class, int avail) +{ + int which; + char buf[4*MAXUSERS]; + int r[4]; + char ripfile[MAXPATHLEN]; +#ifndef HAVE_FLOCK + struct flock arg; +#endif + + /* + * if ripfd was not opened previously... + * ripfd must stay open after the chroot(~ftp) + */ + + sprintf(ripfile, _PATH_RIPNAMES, class); + + if (ripfd < 0) { + mode_t oldmask; + oldmask = umask(0); + ripfd = open(ripfile, O_RDWR | O_CREAT, 0644); + (void) umask(oldmask); + } + + if (ripfd < 0) { + syslog(LOG_ERR, "cannot open rip file %s: %m", ripfile); + return; + } + +#ifdef HAVE_FLOCK + while (flock(ripfd, LOCK_EX)) { +#ifndef NO_PID_SLEEP_MSGS + syslog(LOG_ERR, "sleeping: flock of rip file failed: %m"); +#endif +#else + arg.l_type = F_WRLCK; + arg.l_whence = arg.l_start = arg.l_len = 0; + while ( -1 == fcntl( ripfd, F_SETLK, &arg) ) { +#ifndef NO_PID_SLEEP_MSGS + syslog(LOG_ERR, "sleeping: fcntl lock of rip file failed: %m"); +#endif +#endif + sleep(1); + } + + lseek(ripfd, (off_t)0, SEEK_SET); + if (read(ripfd, (void *)buf, sizeof(buf)) < sizeof(buf)) + for (which = 0; which < 4*MAXUSERS; buf[which++] = 0) + continue; + + sscanf(remoteaddr,"%d.%d.%d.%d",r,r+1,r+2,r+3); + avail *= 4; + buf[avail] = r[0]; + buf[avail+1] = r[1]; + buf[avail+2] = r[2]; + buf[avail+3] = r[3]; + + lseek(ripfd, (off_t)0, SEEK_SET); + write(ripfd, (void *)buf, sizeof(buf)); +#ifdef HAVE_FLOCK + flock(ripfd, LOCK_UN); +#else + arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0; + fcntl(ripfd, F_SETLK, &arg); +#endif + +} + +/*************************************************************************/ +/* FUNCTION : acl_remhost */ +/* PURPOSE : Remove the current host IP from the list of addresses in */ +/* the specified class. */ +/* ARGUMENTS : The name of the class to remove, the "slot" number */ +/*************************************************************************/ + +void acl_remhost(char *class, int avail) +{ + int which; + char buf[4*MAXUSERS]; + char ripfile[MAXPATHLEN]; +#ifndef HAVE_FLOCK + struct flock arg; +#endif + + /* + * if ripfd was not opened previously... + * ripfd must stay open after the chroot(~ftp) + */ + + sprintf(ripfile, _PATH_RIPNAMES, class); + + if (ripfd < 0) { + mode_t oldmask; + oldmask = umask(0); + ripfd = open(ripfile, O_RDWR | O_CREAT, 0644); + (void) umask(oldmask); + } + + if (ripfd < 0) { + syslog(LOG_ERR, "cannot open rip file %s: %m", ripfile); + return; + } + +#ifdef HAVE_FLOCK + while (flock(ripfd, LOCK_EX)) { +#ifndef NO_PID_SLEEP_MSGS + syslog(LOG_ERR, "sleeping: flock of rip file failed: %m"); +#endif +#else + arg.l_type = F_WRLCK; + arg.l_whence = arg.l_start = arg.l_len = 0; + while ( -1 == fcntl( ripfd, F_SETLK, &arg) ) { +#ifndef NO_PID_SLEEP_MSGS + syslog(LOG_ERR, "sleeping: fcntl lock of rip file failed: %m"); +#endif +#endif + sleep(1); + } + + lseek(ripfd, (off_t)0, SEEK_SET); + if (read(ripfd, (void *)buf, sizeof(buf)) < sizeof(buf)) + for (which = 0; which < 4*MAXUSERS; buf[which++] = 0) + continue; + + avail *= 4; + buf[avail] = buf[avail+1] = buf[avail+2] = buf[avail+3] = 0; + + lseek(ripfd, (off_t)0, SEEK_SET); + write(ripfd, (void *)buf, sizeof(buf)); +#ifdef HAVE_FLOCK + flock(ripfd, LOCK_UN); +#else + arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0; + fcntl(ripfd, F_SETLK, &arg); +#endif + + close(ripfd); + ripfd = -1; +} +#endif + /*************************************************************************/ /* FUNCTION : acl_join */ /* PURPOSE : Add the current process to the list of processes in the */ @@ -1292,6 +1553,9 @@ arg.l_whence = arg.l_start = arg.l_len = 0; fcntl(pidfd, F_SETLK, &arg); #endif +#ifdef HOST_LIMIT + acl_addhost(class, avail); +#endif } @@ -1370,6 +1634,9 @@ } else if (buf[which] == procid) { buf[which] = 0; +#ifdef HOST_LIMIT + acl_remhost(class, which); +#endif } } @@ -1509,6 +1776,23 @@ #endif #endif #endif + +#ifdef HOST_LIMIT + limit = acl_gethostlimit(class, msgfile); + if ((limit != -1) && (acl_counthosts(class) >= limit)) { +#ifdef LOG_TOOMANY + syslog(LOG_NOTICE, "ACCESS DENIED (host limit %d; class %s) TO %s", + limit, class, remoteident); +#endif +#ifndef HELP_CRACKERS + memcpy (DelayedMessageFile, msgfile, sizeof (msgfile)); +#else + pr_mesg(msgcode, msgfile); +#endif + return (-1); + } +#endif + /* if no limits defined, no limits apply -- access OK */ limit = acl_getlimit(class, msgfile); diff -ru wu-ftpd-2.6.2/src/pathnames.h.in wu-ftpd-2.6.2hl/src/pathnames.h.in --- wu-ftpd-2.6.2/src/pathnames.h.in 2000-07-01 20:04:21.000000000 +0200 +++ wu-ftpd-2.6.2hl/src/pathnames.h.in 2002-03-28 02:09:44.000000000 +0100 @@ -63,6 +63,7 @@ ** _PATH_FTPSERVERS ** _PATH_EXECPATH ** _PATH_PIDNAMES + ** _PATH_RlPNAMES ** _PATH_UTMP ** _PATH_WTMP ** _PATH_LASTLOG @@ -97,6 +98,7 @@ /* _PATH_FTPD_PIDFILE is only used if DAEMON is defined */ #define _PATH_PIDNAMES "@PIDDIR@/ftp.pids-%s" +#define _PATH_RIPNAMES "@PIDDIR@/ftp.rips-%s" #define _PATH_FTPD_PID "@PIDDIR@/ftpd.pid" #define _PATH_XFERLOG "@LOGDIR@/xferlog" diff -ru wu-ftpd-2.6.2/src/pathnames.h.noac wu-ftpd-2.6.2hl/src/pathnames.h.noac --- wu-ftpd-2.6.2/src/pathnames.h.noac 2000-07-01 20:17:39.000000000 +0200 +++ wu-ftpd-2.6.2hl/src/pathnames.h.noac 2002-03-28 02:09:44.000000000 +0100 @@ -121,13 +121,16 @@ #ifdef USE_VAR #ifdef USE_PID #define _PATH_PIDNAMES "/var/pid/ftp.pids-%s" +#define _PATH_RIPNAMES "/var/pid/ftp.rips-%s" #define _PATH_FTPD_PID "/var/pid/ftpd.pid" #else #ifdef VAR_RUN #define _PATH_PIDNAMES "/var/run/ftp.pids-%s" +#define _PATH_RIPNAMES "/var/run/ftp.rips-%s" #define _PATH_FTPD_PID "/var/run/ftpd.pid" #else #define _PATH_PIDNAMES "/var/adm/ftp.pids-%s" +#define _PATH_RIPNAMES "/var/adm/ftp.rips-%s" #define _PATH_FTPD_PID "/var/adm/ftpd.pid" #endif #endif @@ -139,10 +142,12 @@ #else #ifdef USE_USR #define _PATH_PIDNAMES "/usr/adm/ftp.pids-%s" +#define _PATH_RIPNAMES "/usr/adm/ftp.rips-%s" #define _PATH_FTPD_PID "/usr/adm/ftpd.pid" #define _PATH_XFERLOG "/usr/adm/xferlog" #else #define _PATH_PIDNAMES "/usr/local/lib/ftpd/pids/%s" +#define _PATH_RIPNAMES "/usr/local/lib/ftpd/pids/%s.rips" #define _PATH_FTPD_PID "/usr/local/lib/ftpd/pids/ftpd.pid" #define _PATH_XFERLOG "/usr/local/logs/xferlog" #endif .