From 411365f15d431cee967e5d16b04ee1687c259a3c Mon Sep 17 00:00:00 2001 From: yar Date: Sun, 26 Jan 2003 19:02:56 +0000 Subject: Extend the format of /etc/ftpchroot so an alternative chroot directory can be specified for a user or a group. Add the manpage ftpchroot(5) since the file's format has grown complex enough. PR: bin/45327 Portions submitted by: Hideki SAKAMOTO MFC after: 1 week --- libexec/ftpd/Makefile | 2 +- libexec/ftpd/ftpchroot.5 | 109 +++++++++++++++++++++++++++++++++++++++++++++++ libexec/ftpd/ftpd.8 | 7 ++- libexec/ftpd/ftpd.c | 77 +++++++++++++++++++++++---------- 4 files changed, 171 insertions(+), 24 deletions(-) create mode 100644 libexec/ftpd/ftpchroot.5 (limited to 'libexec') diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index 0a3ffc5..b6c8ed9 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -2,7 +2,7 @@ # $FreeBSD$ PROG= ftpd -MAN= ftpd.8 +MAN= ftpd.8 ftpchroot.5 SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c CFLAGS+=-DSETPROCTITLE -DLOGIN_CAP -DVIRTUAL_HOSTING diff --git a/libexec/ftpd/ftpchroot.5 b/libexec/ftpd/ftpchroot.5 new file mode 100644 index 0000000..8c8f629 --- /dev/null +++ b/libexec/ftpd/ftpchroot.5 @@ -0,0 +1,109 @@ +.\" Copyright (c) 2003 FreeBSD Project +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 26, 2003 +.Dt FTPCHROOT 5 +.Os +.Sh NAME +.Nm ftpchroot +.Nd list users and groups subject to FTP access restrictions +.Sh DESCRIPTION +The file +.Nm +is read by +.Xr ftpd 8 +at the beginning of an FTP session, after having authenticated the user. +Each line in +.Nm +corresponds to a user or group. If a line in +.Nm +matches the current user or a group he is a member of, +access restrictions will be applied to this +session by changing its root directory with +.Xr chroot 2 +to that specified on the line or to the user's login directory. +.Pp +The order of records in +.Nm +is important because the first match will be used. +Fields on each line are separated by tabs or spaces. +.Pp +The first field specifies a user or group name. +If it is prefixed by an +.Qq at +sign, +.Ql \&@ , +it specifies a group name; +the line will match each user who is a member of this group. +As a special case, a single +.Ql \&@ +in this field will match any user. +A username is specified otherwise. +.Pp +The optional second field describes the directory for the user +or each member of the group to be locked up in using +.Xr chroot 2 . +If it is not an absolute pathname, then it will be relative +to the user's login directory. +Be this field omitted, the user's login directory will be used. +.Sh FILES +.Bl -tag -width /etc/ftpchroot -compact +.It Pa /etc/ftpchroot +.El +.Sh EXAMPLES +These lines in +.Nm +will lock up the user +.Qq webuser +and each member of the group +.Qq hostee +in their respective login directories: +.Bd -literal -offset indent +webuser +@hostee +.Ed +.Pp +And this line will lock up the user +.Qq joe +in +.Pa /var/spool/ftp : +.Bd -literal -offset indent +joe /var/spool/ftp +.Ed +.Pp +And finally the following line will lock up every user connecting +through FTP in his respective +.Pa \&~/public_html , +thus lowering possible impact on the system +from intrinsic insecurity of FTP: +.Bd -literal -offset indent +@ public_html +.Ed +.Sh SEE ALSO +.Xr chroot 2 , +.Xr group 5 , +.Xr passwd 5 , +.Xr ftpd 8 . diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index b995b8a..9d9fd82 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -358,13 +358,17 @@ If the user name appears in the file or the user is a member of a group with a group entry in this file, i.e. one prefixed with .Ql \&@ , -the session's root will be changed to the user's login directory by +the session's root will be changed to the directory specified +in this file or to the user's login directory by .Xr chroot 2 as for an .Dq anonymous or .Dq ftp account (see next item). +See +.Xr ftpchroot 5 +for a detailed description of the format of this file. This facility may also be triggered by enabling the boolean "ftp-chroot" capability in .Xr login.conf 5 . @@ -518,6 +522,7 @@ Default place for session logs. .Xr key 1 , .Xr umask 2 , .Xr getusershell 3 , +.Xr ftpchroot 5 , .Xr login.conf 5 , .Xr inetd 8 , .Xr syslogd 8 diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index ca1e348..71fca60 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -239,7 +239,7 @@ static void selecthost(union sockunion *); static void ack(char *); static void sigurg(int); static void myoob(void); -static int checkuser(char *, char *, int); +static int checkuser(char *, char *, int, char **); static FILE *dataconn(char *, off_t, char *); static void dolog(struct sockaddr *); static char *curdir(void); @@ -1000,8 +1000,8 @@ user(char *name) guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { - if (checkuser(_PATH_FTPUSERS, "ftp", 0) || - checkuser(_PATH_FTPUSERS, "anonymous", 0)) + if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL) || + checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL)) reply(530, "User %s access denied.", name); #ifdef VIRTUAL_HOSTING else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { @@ -1032,7 +1032,7 @@ user(char *name) break; endusershell(); - if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) { + if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1, NULL)) { reply(530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, @@ -1069,10 +1069,12 @@ user(char *name) } /* - * Check if a user is in the file "fname" + * Check if a user is in the file "fname", + * return a pointer to a malloc'd string with the rest + * of the matching line in "residue" if not NULL. */ static int -checkuser(char *fname, char *name, int pwset) +checkuser(char *fname, char *name, int pwset, char **residue) { FILE *fd; int found = 0; @@ -1106,26 +1108,40 @@ checkuser(char *fname, char *name, int pwset) int i = 0; struct group *grp; - if ((grp = getgrnam(p+1)) == NULL) - goto nextline; - /* - * Check user's default group - */ - if (pwset && grp->gr_gid == pw->pw_gid) + if (p[1] == '\0') /* single @ matches anyone */ found = 1; - /* - * Check supplementary groups - */ - while (!found && grp->gr_mem[i]) - found = strcmp(name, - grp->gr_mem[i++]) - == 0; + else { + if ((grp = getgrnam(p+1)) == NULL) + goto nextline; + /* + * Check user's default group + */ + if (pwset && grp->gr_gid == pw->pw_gid) + found = 1; + /* + * Check supplementary groups + */ + while (!found && grp->gr_mem[i]) + found = strcmp(name, + grp->gr_mem[i++]) + == 0; + } } /* * Otherwise, just check for username match */ else found = strcmp(p, name) == 0; + /* + * Save the rest of line to "residue" if matched + */ + if (found && residue) { + if ((p = strtok(NULL, "")) != NULL) { + if ((*residue = strdup(p)) == NULL) + fatalerror("Ran out of memory."); + } else + *residue = NULL; + } nextline: if (mp) free(mp); @@ -1331,6 +1347,7 @@ pass(char *passwd) #ifdef USE_PAM int e; #endif + char *chrootdir; char *xpasswd; if (logged_in || askpasswd == 0) { @@ -1447,10 +1464,11 @@ skip: stats = 0; dochroot = + checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &chrootdir) #ifdef LOGIN_CAP /* Allow login.conf configuration as well */ - login_getcapbool(lc, "ftp-chroot", 0) || + || login_getcapbool(lc, "ftp-chroot", 0) #endif - checkuser(_PATH_FTPCHROOT, pw->pw_name, 1); + ; if (guest) { /* * We MUST do a chdir() after the chroot. Otherwise @@ -1462,10 +1480,25 @@ skip: goto bad; } } else if (dochroot) { - if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { + if (chrootdir) { /* chroot dir set in ftpchroot(5) */ + if (chrootdir[0] != '/') { /* relative to homedir */ + char *p; + + asprintf(&p, "%s/%s", pw->pw_dir, chrootdir); + if (p == NULL) + fatalerror("Ran out of memory."); + free(chrootdir); + chrootdir = p; + } + } else + if ((chrootdir = strdup(pw->pw_dir)) == NULL) + fatalerror("Ran out of memory."); + if (chroot(chrootdir) < 0 || chdir("/") < 0) { reply(550, "Can't change root."); + free(chrootdir); goto bad; } + free(chrootdir); } else if (chdir(pw->pw_dir) < 0) { if (chdir("/") < 0) { reply(530, "User %s: can't change directory to %s.", -- cgit v1.1