From ea4e54b942da4ebc3d980ef5199c496ff500a652 Mon Sep 17 00:00:00 2001 From: David Nugent Date: Tue, 29 Apr 1997 12:42:08 +0000 Subject: [PATCH] Adds anon ftp virtual host capability to ftpd, using /etc/ftphosts for definition of a system's virtual hosts. --- libexec/ftpd/Makefile | 4 +- libexec/ftpd/ftpd.8 | 51 ++++++++- libexec/ftpd/ftpd.c | 226 +++++++++++++++++++++++++++++++++++++-- libexec/ftpd/pathnames.h | 7 +- 4 files changed, 271 insertions(+), 17 deletions(-) diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index 6bb3bdb734cb..49e98d508309 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -1,11 +1,11 @@ # @(#)Makefile 8.2 (Berkeley) 4/4/94 -# $Id: Makefile,v 1.20 1997/04/23 04:56:39 davidn Exp $ +# $Id: Makefile,v 1.21 1997/04/26 12:12:10 davidn Exp $ PROG= ftpd MAN8= ftpd.8 SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c -CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -Wall +CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall LDADD= -lskey -lmd -lcrypt -lutil DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL} diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 6bea61c295fe..b3fa6bdaa620 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 -.\" $Id: ftpd.8,v 1.16 1997/04/26 12:23:51 davidn Exp $ +.\" $Id: ftpd.8,v 1.17 1997/04/27 08:29:21 davidn Exp $ .\" .Dd April 19, 1994 .Dt FTPD 8 @@ -286,8 +286,8 @@ This facility may also be triggered by enabling the boolean "ftp-chroot" capability in .Xr login.conf 5 . However, the user must still supply a password. -This feature is intended as a compromise between a fully anonymous account -and a fully privileged account. +This feature is intended as a compromise between a fully anonymous +account and a fully privileged account. The account should also be set up as for an anonymous account. .It If the user name is @@ -357,6 +357,51 @@ can then place files which are to be accessible via the anonymous account in this directory. .El .Pp +If the system has multiple IP addresses, +.Nm ftpd +supports the idea of virtual hosts, which provides the ability to +define multiple anonymous ftp areas, each one allocated to a different +internet address. +The file +.Pa /etc/ftphosts +contains information pertaining to each of the virtual hosts. +Each host is defined on its own line which contains a number of +fields separated by whitespace: +.Bl -tag -offset indent -width hostname +.It hostname +Contains the hostname or IP address of the virtual host. +.It user +Contains a user record in the system password file. +As with normal anonymous ftp, this user's access uid, gid and group +memberships determine file access to the anonymous ftp area. +The anonymous ftp area (to which any user is chrooted on login) +is determined by the home directory defined for the account. +User id and group for any ftp account may be the same as for the +standard ftp user. +.It statfile +File to which all file transfers are logged, which +defaults to +.Pa /var/log/ftpd . +.It welcome +This file is the welcome message displayed before the server ready +prompt. +It defaults to +.Pa /etc/ftpwelcome . +.It motd +This file is displayed after the user logs in. +It defaults to +.Pa /etc/ftpmotd . +.El +.Pp +Defining a virtual host for the primary IP address or hostname +changes the default for ftp logins to that address. +The 'user', 'statfile', 'welcome' and 'motd' fields may be left +blank, or a single hypen '-' used to indicate that the default +value is to be used. +.Pp +As with any anonymous login configuration, due care must be given +to setup and maintenance to guard against security related problems. +.Pp If compiled with the .Em INTERNAL_LS option, diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 65e5b9743b0e..82c10dd7ba00 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ftpd.c,v 1.36 1997/04/26 12:12:10 davidn Exp $ + * $Id: ftpd.c,v 1.37 1997/04/27 08:29:21 davidn Exp $ */ #if 0 @@ -151,7 +151,23 @@ off_t byte_count; #endif int defumask = CMASK; /* default umask value */ char tmpline[7]; +#ifdef VIRTUAL_HOSTING +char *hostname; +char *ftpuser; + +static struct ftphost { + struct ftphost *next; + struct in_addr hostaddr; + char *hostname; + char *anonuser; + char *statfile; + char *welcome; + char *loginmsg; +} *thishost, *firsthost; + +#else char hostname[MAXHOSTNAMELEN]; +#endif char remotehost[MAXHOSTNAMELEN]; char *ident = NULL; @@ -214,6 +230,10 @@ char addr_string[20]; /* XXX */ cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ } +#ifdef VIRTUAL_HOSTING +static void inithosts __P((void)); +static void selecthost __P((struct in_addr *)); +#endif static void ack __P((char *)); static void myoob __P((int)); static int checkuser __P((char *, char *)); @@ -341,6 +361,9 @@ main(argc, argv, envp) } } +#ifdef VIRTUAL_HOSTING + inithosts(); +#endif (void) freopen(_PATH_DEVNULL, "w", stderr); /* @@ -450,6 +473,10 @@ main(argc, argv, envp) syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } +#ifdef VIRTUAL_HOSTING + /* select our identity from virtual host table */ + selecthost(&ctrl_addr.sin_addr); +#endif #ifdef IP_TOS tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) @@ -493,7 +520,11 @@ main(argc, argv, envp) reply(530, "System not available."); exit(0); } +#ifdef VIRTUAL_HOSTING + if ((fd = fopen(thishost->welcome, "r")) != NULL) { +#else if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { +#endif while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; @@ -503,7 +534,9 @@ main(argc, argv, envp) (void) fclose(fd); /* reply(220,) must follow */ } +#ifndef VIRTUAL_HOSTING (void) gethostname(hostname, sizeof(hostname)); +#endif reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); for (;;) @@ -521,6 +554,147 @@ lostconn(signo) dologout(-1); } +#ifdef VIRTUAL_HOSTING +/* + * read in virtual host tables (if they exist) + */ + +static void +inithosts() +{ + FILE *fp; + char *cp; + struct hostent *hp; + struct ftphost *hrp, *lhrp; + char line[1024]; + + /* + * Fill in the default host information + */ + if (gethostname(line, sizeof(line)) < 0) + line[0] = '\0'; + if ((hrp = malloc(sizeof(struct ftphost))) == NULL || + (hrp->hostname = strdup(line)) == NULL) + fatal("Ran out of memory."); + memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr); + if ((hp = gethostbyname(hrp->hostname)) != NULL) + (void) memcpy(&hrp->hostaddr, + hp->h_addr_list[0], + sizeof(hrp->hostaddr)); + hrp->statfile = _PATH_FTPDSTATFILE; + hrp->welcome = _PATH_FTPWELCOME; + hrp->loginmsg = _PATH_FTPLOGINMESG; + hrp->anonuser = "ftp"; + hrp->next = NULL; + thishost = firsthost = lhrp = hrp; + if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { + while (fgets(line, sizeof(line), fp) != NULL) { + int i; + + if ((cp = strchr(line, '\n')) == NULL) { + /* ignore long lines */ + while (fgets(line, sizeof(line), fp) != NULL && + strchr(line, '\n') == NULL) + ; + continue; + } + *cp = '\0'; + cp = strtok(line, " \t"); + /* skip comments and empty lines */ + if (cp == NULL || line[0] == '#') + continue; + /* first, try a standard gethostbyname() */ + if ((hp = gethostbyname(cp)) == NULL) + continue; + for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { + if (memcmp(&hrp->hostaddr, + hp->h_addr_list[0], + sizeof(hrp->hostaddr)) == 0) + break; + } + if (hrp == NULL) { + if ((hrp = malloc(sizeof(struct ftphost))) == NULL) + continue; + /* defaults */ + hrp->statfile = _PATH_FTPDSTATFILE; + hrp->welcome = _PATH_FTPWELCOME; + hrp->loginmsg = _PATH_FTPLOGINMESG; + hrp->anonuser = "ftp"; + hrp->next = NULL; + lhrp->next = hrp; + lhrp = hrp; + } + (void) memcpy(&hrp->hostaddr, + hp->h_addr_list[0], + sizeof(hrp->hostaddr)); + /* + * determine hostname to use. + * force defined name if it is a valid alias + * otherwise fallback to primary hostname + */ + if ((hp = gethostbyaddr((char*)&hrp->hostaddr, + sizeof(hrp->hostaddr), + AF_INET)) != NULL) { + if (strcmp(cp, hp->h_name) != 0) { + if (hp->h_aliases == NULL) + cp = hp->h_name; + else { + i = 0; + while (hp->h_aliases[i] && + strcmp(cp, hp->h_aliases[i]) != 0) + ++i; + if (hp->h_aliases[i] == NULL) + cp = hp->h_name; + } + } + } + hrp->hostname = strdup(cp); + /* ok, now we now peel off the rest */ + i = 0; + while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) { + if (*cp != '-' && (cp = strdup(cp)) != NULL) { + switch (i) { + case 0: /* anon user permissions */ + hrp->anonuser = cp; + break; + case 1: /* statistics file */ + hrp->statfile = cp; + break; + case 2: /* welcome message */ + hrp->welcome = cp; + break; + case 3: /* login message */ + hrp->loginmsg = cp; + break; + } + } + ++i; + } + } + (void) fclose(fp); + } +} + +static void +selecthost(a) + struct in_addr *a; +{ + struct ftphost *hrp; + + hrp = thishost = firsthost; /* default */ + while (hrp != NULL) { + if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + thishost = hrp; + break; + } + hrp = hrp->next; + } + /* setup static variables as appropriate */ + hostname = thishost->hostname; + ftpuser = thishost->anonuser; +} +#endif + /* * Helper function for sgetpwnam(). */ @@ -606,7 +780,11 @@ user(name) if (checkuser(_PATH_FTPUSERS, "ftp") || checkuser(_PATH_FTPUSERS, "anonymous")) reply(530, "User %s access denied.", name); +#ifdef VIRTUAL_HOSTING + else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { +#else else if ((pw = sgetpwnam("ftp")) != NULL) { +#endif guest = 1; askpasswd = 1; reply(331, @@ -820,7 +998,11 @@ skip: logged_in = 1; if (guest && stats && statfd < 0) +#ifdef VIRTUAL_HOSTING + if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) +#else if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) +#endif stats = 0; dochroot = @@ -866,7 +1048,11 @@ skip: * Display a login message, if it exists. * N.B. reply(230,) must follow the message. */ +#ifdef VIRTUAL_HOSTING + if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { +#else if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { +#endif char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), fd) != NULL) { @@ -886,10 +1072,18 @@ skip: reply(230, "Guest login ok, access restrictions apply."); #ifdef SETPROCTITLE - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous/%.*s", remotehost, - sizeof(proctitle) - sizeof(remotehost) - - sizeof(": anonymous/"), passwd); +#ifdef VIRTUAL_HOSTING + if (thishost != firsthost) + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous(%s)/%.*s", remotehost, hostname, + sizeof(proctitle) - sizeof(remotehost) - + sizeof(": anonymous/"), passwd); + else +#endif + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous/%.*s", remotehost, + sizeof(proctitle) - sizeof(remotehost) - + sizeof(": anonymous/"), passwd); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) @@ -899,7 +1093,7 @@ skip: reply(230, "User %s logged in.", pw->pw_name); #ifdef SETPROCTITLE snprintf(proctitle, sizeof(proctitle), - "%s: %s", remotehost, pw->pw_name); + "%s: %s", remotehost, pw->pw_name); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) @@ -1695,12 +1889,26 @@ dolog(sin) (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost)); #ifdef SETPROCTITLE - snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); +#ifdef VIRTUAL_HOSTING + if (thishost != firsthost) + snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", + remotehost, hostname); + else +#endif + snprintf(proctitle, sizeof(proctitle), "%s: connected", + remotehost); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "connection from %s", remotehost); + if (logging) { +#ifdef VIRTUAL_HOSTING + if (thishost != firsthost) + syslog(LOG_INFO, "connection from %s (to %s)", + remotehost, hostname); + else +#endif + syslog(LOG_INFO, "connection from %s", remotehost); + } } /* diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h index 5b36082efe23..d2f8b737b570 100644 --- a/libexec/ftpd/pathnames.h +++ b/libexec/ftpd/pathnames.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 - * $Id: pathnames.h,v 1.8 1997/02/22 14:21:29 peter Exp $ + * $Id: pathnames.h,v 1.9 1997/04/26 12:12:10 davidn Exp $ */ #include @@ -39,5 +39,6 @@ #define _PATH_FTPCHROOT "/etc/ftpchroot" #define _PATH_FTPWELCOME "/etc/ftpwelcome" #define _PATH_FTPLOGINMESG "/etc/ftpmotd" -#define _PATH_FTPDSTATFILE "/var/log/ftpd" -#define _PATH_LS "/bin/ls" +#define _PATH_FTPHOSTS "/etc/ftphosts" +#define _PATH_FTPDSTATFILE "/var/log/ftpd" +#define _PATH_LS "/bin/ls"