Date: Thu, 4 Jan 2001 14:00:12 -0800 (PST) From: Daniel Hagan <dhagan@colltech.com> To: freebsd-bugs@FreeBSD.org Subject: Re: bin/23944: [PATCH] Patch for ftpd to add a cd after the chroot. Message-ID: <200101042200.f04M0CW11887@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/23944; it has been noted by GNATS. From: Daniel Hagan <dhagan@colltech.com> To: freebsd-gnats-submit@FreeBSD.org, fpscha@ns1.via-net-works.net.ar Cc: Subject: Re: bin/23944: [PATCH] Patch for ftpd to add a cd after the chroot. Date: Thu, 04 Jan 2001 16:58:24 -0500 Here's a patch that addresses several different PRs and discussions recently concerning ftpd. Available at http://vtopus.cs.vt.edu/~dhagan/freebsd/ftpd.patch Daniel Index: ftpcmd.y =================================================================== RCS file: /raid/ncvs/src/libexec/ftpd/ftpcmd.y,v retrieving revision 1.19 diff -u -r1.19 ftpcmd.y --- ftpcmd.y 2000/12/16 19:19:19 1.19 +++ ftpcmd.y 2001/01/04 20:16:35 @@ -91,7 +91,10 @@ extern int transflag; extern char tmpline[]; extern int readonly; +extern int readonly_user; extern int noepsv; +extern int dochroot; +extern char *cd_dir, *chroot_dir; off_t restart_point; @@ -505,8 +508,11 @@ | CWD check_login CRLF { if ($2) { - if (guest) - cwd("/"); + if (guest || dochroot) + if (cd_dir != NULL) + cwd(cd_dir); + else + cwd("/"); else cwd(pw->pw_dir); } @@ -979,6 +985,10 @@ { if (readonly) { reply(202, "Command ignored. Server is in readonly mode."); + $$ = 0; + } + else if (readonly_user) { + reply(202, "Command ignored. User is in readonly session."); $$ = 0; } else Index: ftpd.8 =================================================================== RCS file: /raid/ncvs/src/libexec/ftpd/ftpd.8,v retrieving revision 1.36 diff -u -r1.36 ftpd.8 --- ftpd.8 2000/12/18 08:33:25 1.36 +++ ftpd.8 2001/01/04 20:23:27 @@ -158,6 +158,10 @@ .It Fl r Put server in read-only mode. All commands which may modify the local filesystem are disabled. +Read-only mode may be set on a per account basis in +.Xr login.conf 5 +with the boolean capability +.Dq ftp-readonly . .It Fl E Disable the EPSV command. This is useful for servers behind older firewalls. @@ -258,8 +262,12 @@ .Pp The ftp server will abort an active file transfer only when the ABOR -command is preceded by a Telnet "Interrupt Process" (IP) -signal and a Telnet "Synch" signal in the command Telnet stream, +command is preceded by a Telnet +.Dq "Interrupt Process" +.Pq IP +signal and a Telnet +.Dq Synch +signal in the command Telnet stream, as described in Internet RFC 959. If a STAT @@ -299,7 +307,8 @@ .It The login name must not be a member of a group specified in the file .Pa /etc/ftpusers . -Entries in this file interpreted as group names are prefixed by an "at" +Entries in this file interpreted as group names are prefixed by an +.Dq at .Ql \&@ sign. .It @@ -311,14 +320,19 @@ 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 user's login directory +.Pq "up to the first /./" +by .Xr chroot 2 as for an .Dq anonymous or .Dq ftp account (see next item). -This facility may also be triggered by enabling the boolean "ftp-chroot" +The user is placed into the directory that remains after stripping the +former from the user's login directory. +This facility may also be triggered by enabling the boolean +.Dq ftp-chroot capability in .Xr login.conf 5 . However, the user must still supply a password. Index: ftpd.c =================================================================== RCS file: /raid/ncvs/src/libexec/ftpd/ftpd.c,v retrieving revision 1.72 diff -u -r1.72 ftpd.c --- ftpd.c 2000/12/20 03:34:54 1.72 +++ ftpd.c 2001/01/04 20:11:48 @@ -140,6 +140,7 @@ int anon_only = 0; /* Only anonymous ftp allowed */ int guest; int dochroot; +char *cd_dir = NULL, *chroot_dir = NULL; int stats; int statfd = -1; int type; @@ -149,6 +150,7 @@ int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ int readonly=0; /* Server is in readonly mode. */ +int readonly_user = 0; /* User's session is readonly. */ int noepsv=0; /* EPSV command is disabled. */ sig_atomic_t transflag; off_t file_size; @@ -188,6 +190,9 @@ char *pid_file = NULL; +/* WARNING: FTP_CHROOT_SEPARATOR *MUST* end in / */ +#define FTP_CHROOT_SEPARATOR "/./" + /* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This @@ -251,6 +256,7 @@ static char *sgetsave __P((char *)); static void reapchild __P((int)); static void logxfer __P((char *, long, long)); +static void get_chroot_and_cd_dirs __P((char *, char **, char **)); static char * curdir() @@ -1038,6 +1044,10 @@ logged_in = 0; guest = 0; dochroot = 0; + readonly_user = 0; + free(chroot_dir); + free(cd_dir); + chroot_dir = cd_dir = NULL; } #if !defined(NOPAM) @@ -1291,19 +1301,23 @@ login_getcapbool(lc, "ftp-chroot", 0) || #endif checkuser(_PATH_FTPCHROOT, pw->pw_name, 1); - if (guest) { +#ifdef LOGIN_CAP /* Check for ftp-readonly */ + readonly_user = login_getcapbool(lc, "ftp-readonly", 0); +#endif + if (guest || dochroot) { /* * We MUST do a chdir() after the chroot. Otherwise * the old current directory will be accessible as "." * outside the new root! */ - if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { - reply(550, "Can't set guest privileges."); - goto bad; - } - } else if (dochroot) { - if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { - reply(550, "Can't change root."); + get_chroot_and_cd_dirs(pw->pw_dir, &chroot_dir, &cd_dir); + /* + * Do not free chroot_dir & cd_dir b/c they are used in + * processing CWD commands from client. They should be + * free'd during a user logout. + */ + if (chroot(chroot_dir) < 0 || chdir(cd_dir) < 0) { + reply(550, guest ? "Can't set guest privileges." : "Can't change root."); goto bad; } } else if (chdir(pw->pw_dir) < 0) { @@ -2802,5 +2816,50 @@ ctime(&now)+4, ident, remotehost, path, name, size, now - start + (now == start)); write(statfd, buf, strlen(buf)); + } +} + +/* + * Make a pointer to the chroot dir and another to the cd dir. + * The first is all the path up to the first FTP_CHROOT_SEPARATOR. + * The later is the remaining chars, not including the FTP_CHROOT_SEPARATOR, + * but prepending a '/', if FTP_CHROOT_SEPARATOR is found. + * Otherwise, return user_home_dir as chroot_dir and "/" as cd_dir. + */ +static void +get_chroot_and_cd_dirs(user_home_dir, chroot_dir, cd_dir) + char *user_home_dir; + char **chroot_dir; + char **cd_dir; +{ + char *p; + + /* Make a pointer to first character of string FTP_CHROOT_SEPARATOR + inside user_home_dir. */ + p = (char *) strstr(user_home_dir, FTP_CHROOT_SEPARATOR); + if (p == NULL) { + /* + * There is not FTP_CHROOT_SEPARATOR string inside + * user_home_dir. Return user_home_dir as chroot_dir, + * and "/" as cd_dir. + */ + if ((*chroot_dir = (char *) strdup(user_home_dir)) == NULL) + fatal("Ran out of memory."); + if ((*cd_dir = (char *) strdup("/")) == NULL) + fatal("Ran out of memory."); + } else { + /* + * Use strlen(user_home_dir) as maximun length for + * both cd_dir and chroot_dir, as both are substrings of + * user_home_dir. + */ + if ((*chroot_dir = malloc(strlen(user_home_dir))) == NULL) + fatal("Ran out of memory."); + if ((*cd_dir = malloc(strlen(user_home_dir))) == NULL) + fatal("Ran out of memory."); + (void) strncpy(*chroot_dir, user_home_dir, p-user_home_dir); + /* Skip FTP_CHROOT_SEPARATOR (except the last /). */ + p += strlen(FTP_CHROOT_SEPARATOR)-1; + (void) strncpy(*cd_dir, p, strlen(p)); } } To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200101042200.f04M0CW11887>