Subject: per-user initial directories for ftpd
To: None <tech-userlevel@netbsd.org>
From: Michael Graff <explorer@flame.org>
List: tech-userlevel
Date: 04/27/1999 14:19:08
I had a need to allow per-user initial directories, which ideally
would be fully anonymous sites, but that is harder. I don't allow
normal users to ftp in at all, since I don't want cleartext passwords
flying around the net.
Logging in with
ftp.userid
or
anonymous.userid
depending on which is in /etc/ftpusers, will start the user out
somewhere other than /.
Note that the "userid" portion need not match a real user on your
system. It could be a random string, which would keep people from
guessing them easily and just cd'ing into them.
I start ftpd with -A userftp, and when ftp.explorer comes in, the
initial directory is ~ftp/userftp/explorer rather than ~ftp/.
Here are the diffs. If this isn't considered Really Bad, would others
find it, or something like it, useful?
/etc/ftpd.conf changes are that a new class, "guestish" is added, for
lack of a better name. The defaults are identical to the defaults for
class "guest" but having them separate allows different umasks, etc.
--Michael
Index: conf.c
===================================================================
RCS file: /cvsroot/src/libexec/ftpd/conf.c,v
retrieving revision 1.18
diff -u -r1.18 conf.c
--- conf.c 1999/02/24 16:45:13 1.18
+++ conf.c 1999/04/27 19:53:44
@@ -110,7 +110,8 @@
curclass.timeout = 900; /* 15 minutes */
curclass.umask = 027;
- if (strcasecmp(findclass, "guest") == 0) {
+ if (strcasecmp(findclass, CLASS_GUEST) == 0
+ || strcasecmp(findclass, CLASS_GUESTISH) == 0) {
curclass.modify = 0;
curclass.umask = 0707;
}
Index: extern.h
===================================================================
RCS file: /cvsroot/src/libexec/ftpd/extern.h,v
retrieving revision 1.15
diff -u -r1.15 extern.h
--- extern.h 1998/12/28 04:54:01 1.15
+++ extern.h 1999/04/27 19:53:44
@@ -71,6 +71,7 @@
#define CLASS_CHROOT "chroot"
#define CLASS_GUEST "guest"
+#define CLASS_GUESTISH "guestish"
#define CLASS_REAL "real"
struct ftpconv {
@@ -102,6 +103,7 @@
extern int debug;
extern int form;
extern int guest;
+extern int guestish;
extern int hasyyerrored;
extern struct sockaddr_in his_addr;
extern char hostname[];
Index: ftpd.8
===================================================================
RCS file: /cvsroot/src/libexec/ftpd/ftpd.8,v
retrieving revision 1.35
diff -u -r1.35 ftpd.8
--- ftpd.8 1999/03/22 18:25:44 1.35
+++ ftpd.8 1999/04/27 19:53:44
@@ -64,6 +64,16 @@
.Xr chroot 2
into for anonymous logins.
Default is the home directory for the ftp user.
+.It Fl A
+Define the initial directory for "guestish" logins. These are logins of the
+form "ftp.user" or "anonymous.user". They behave mostly as a normal guest
+login, other than the
+.Pa ftpusers
+file must list the exact name used, and
+.Pa ftpd.conf
+should have lines for class "guestish" to define the default permissions, etc.
+This directory should be owned by root:wheel, and be mode 111. The individial
+user directories should be owned by user:ftp, and be mode 770.
.It Fl c
Change the root directory of the configuration files from
.Dq Pa /etc
@@ -304,7 +314,11 @@
.Dq "ftp allow"
to
.Pa /etc/ftpusers
-in order to allow guest logins.
+in order to allow guest logins. If you allow per-user anonymous initial
+directories, you must add those as well, in the form of
+.Dq "ftp.username allow"
+and/or
+.Dq "anonymous.username allow" .
.Ss /etc/ftpchroot
The file
.Pa /etc/ftpchroot
@@ -482,6 +496,8 @@
and
.Dq ftp
users.
+.It Sy guestish
+Per-user initial guest login.
.It Sy all
Matches any class.
.It Sy none
@@ -492,14 +508,16 @@
.Bd -literal -offset indent -compact
checkportcmd none
display none
-maxtimeout all 7200 # 2 hours
+maxtimeout all 7200 # 2 hours
modify all
-modify guest off
+modify guest off
+modify guestish off
notify none
passive all
-timeout all 900 # 15 minutes
-umask all 027
-umask guest 0707
+timeout all 900 # 15 minutes
+umask all 027
+umask guest 0707
+umask guestish 0707
.Ed
.Pp
Directives that appear later in the file override settings by previous
Index: ftpd.c
===================================================================
RCS file: /cvsroot/src/libexec/ftpd/ftpd.c,v
retrieving revision 1.61
diff -u -r1.61 ftpd.c
--- ftpd.c 1998/12/28 04:54:01 1.61
+++ ftpd.c 1999/04/27 19:53:44
@@ -121,6 +121,7 @@
int sflag;
int logging;
int guest;
+int guestish;
int dochroot;
int type;
int form;
@@ -138,6 +139,8 @@
char *tty = ttyline; /* for klogin */
static char *anondir = NULL;
+static char *anonishdir = NULL;
+static char *initialdir = NULL;
static char confdir[MAXPATHLEN];
#if defined(KERBEROS) || defined(KERBEROS5)
@@ -199,12 +202,17 @@
sflag = 0;
(void)strcpy(confdir, _DEFAULT_CONFDIR);
- while ((ch = getopt(argc, argv, "a:c:C:dlst:T:u:v")) != -1) {
+ while ((ch = getopt(argc, argv, "a:A:c:C:dlst:T:u:v")) != -1) {
switch (ch) {
case 'a':
anondir = optarg;
break;
+ case 'A':
+ anonishdir = optarg;
+ syslog(LOG_NOTICE, "anonishdir == %s", anonishdir);
+ break;
+
case 'c':
(void)strncpy(confdir, optarg, sizeof(confdir));
confdir[sizeof(confdir)-1] = '\0';
@@ -395,6 +403,8 @@
user(name)
char *name;
{
+ char *tname;
+
if (logged_in) {
if (guest) {
reply(530, "Can't change user from guest login.");
@@ -410,15 +420,47 @@
kdestroy();
#endif
+ /*
+ * If the name is of the format "ftp.user" or "anonymous.user"
+ * put the user in ~ftp/anonishdir/user initially. "anonishdir"
+ * is set via the -A option on the ftpd command line, and is
+ * relative to ~ftp.
+ *
+ * ~ftp/userftp should be owned by root, and be mode 111.
+ */
guest = 0;
- if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
- if (checkaccess("ftp") || checkaccess("anonymous"))
+ guestish = 0;
+ if (strcmp(name, "ftp") == 0
+ || strcmp(name, "anonymous") == 0
+ || strncmp(name, "ftp.", 4) == 0
+ || strncmp(name, "anonymous.", 10) == 0) {
+ if (checkaccess("ftp")
+ || checkaccess("anonymous")
+ || checkaccess(name))
reply(530, "User %s access denied.", name);
else if ((pw = sgetpwnam("ftp")) != NULL) {
guest = 1;
askpasswd = 1;
reply(331,
- "Guest login ok, type your name as password.");
+ "Guest login ok, type your name as password.");
+ tname = strchr(name, '.');
+ if (tname != NULL && anonishdir != NULL) {
+ tname++;
+ if (*tname != '\0') {
+ char *t;
+
+ t = malloc(strlen(tname)
+ + strlen(anonishdir)
+ + 3);
+ sprintf(t, "/%s/%s",
+ anonishdir, tname);
+ initialdir = t;
+ guestish = 1;
+ syslog(LOG_NOTICE,
+ "Initial directory %s", t);
+ }
+ }
+
} else
reply(530, "User %s unknown.", name);
if (!askpasswd && logging)
@@ -533,6 +575,11 @@
pw = NULL;
logged_in = 0;
guest = 0;
+ guestish = 0;
+ if (initialdir != NULL) {
+ free(initialdir);
+ initialdir = NULL;
+ }
dochroot = 0;
}
@@ -649,8 +696,10 @@
dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name, FALSE, FALSE);
/* parse ftpd.conf, setting up various parameters */
- if (guest)
+ if (guest && !guestish)
parse_conf(CLASS_GUEST);
+ else if (guest && guestish)
+ parse_conf(CLASS_GUESTISH);
else if (dochroot)
parse_conf(CLASS_CHROOT);
else
@@ -663,8 +712,12 @@
* the old current directory will be accessible as "."
* outside the new root!
*/
- if (chroot(anondir ? anondir : pw->pw_dir) < 0 ||
- chdir("/") < 0) {
+ if (chroot(anondir ? anondir : pw->pw_dir) < 0
+ || chdir("/") < 0) {
+ reply(550, "Can't set guest privileges.");
+ goto bad;
+ }
+ if ((initialdir != NULL) && (chdir(initialdir) < 0)) {
reply(550, "Can't set guest privileges.");
goto bad;
}