summaryrefslogtreecommitdiffstats
path: root/libexec/ftpd
diff options
context:
space:
mode:
authorpst <pst@FreeBSD.org>1996-08-05 00:21:15 +0000
committerpst <pst@FreeBSD.org>1996-08-05 00:21:15 +0000
commitfca5bc38ec00d8cb5087036679424e8817d2b782 (patch)
tree12d2de9b0d4e2a49a22dcb35f943c6b5f2e301fb /libexec/ftpd
parent1d37bd800534129c143f350649cec9fa9248e26b (diff)
downloadFreeBSD-src-fca5bc38ec00d8cb5087036679424e8817d2b782.zip
FreeBSD-src-fca5bc38ec00d8cb5087036679424e8817d2b782.tar.gz
Convert STATS and PARANOID to run-time options.
Document the new -R (relax paranoia) option. From NetBSD/Lite2: code and man page cleanups, Kerberos IV hooks (relax, we're still exportable), and /etc/ftpchroot feature for semi-anonymous accounts
Diffstat (limited to 'libexec/ftpd')
-rw-r--r--libexec/ftpd/Makefile9
-rw-r--r--libexec/ftpd/ftpcmd.y30
-rw-r--r--libexec/ftpd/ftpd.847
-rw-r--r--libexec/ftpd/ftpd.c134
-rw-r--r--libexec/ftpd/pathnames.h1
5 files changed, 143 insertions, 78 deletions
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile
index 0780113..50f51cf 100644
--- a/libexec/ftpd/Makefile
+++ b/libexec/ftpd/Makefile
@@ -4,11 +4,18 @@ PROG= ftpd
MAN8= ftpd.8
SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c
-CFLAGS+=-DSETPROCTITLE -DSKEY -DSTATS -DPARANOID
+CFLAGS+=-DSETPROCTITLE -DSKEY -Wall
LDADD= -lskey -lmd -lcrypt -lutil
DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL}
CLEANFILES+=ftpcmd.c y.tab.h
+.if defined(KERBEROS)
+SRCS+= klogin.c
+LDADD+= -lkrb -ldes
+DPADD+= ${LIBKRB} ${LIBDES}
+CFLAGS+=-DKERBEROS
+.endif
+
.include <bsd.prog.mk>
diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y
index aa0a67d..4a162c6 100644
--- a/libexec/ftpd/ftpcmd.y
+++ b/libexec/ftpd/ftpcmd.y
@@ -71,6 +71,7 @@ extern struct sockaddr_in data_dest, his_addr;
extern int logged_in;
extern struct passwd *pw;
extern int guest;
+extern int paranoid;
extern int logging;
extern int type;
extern int form;
@@ -152,19 +153,16 @@ cmd
| PORT check_login SP host_port CRLF
{
if ($2) {
-#ifdef PARANOID
- if ((ntohs(data_dest.sin_port) <
- IPPORT_RESERVED) ||
- memcmp(&data_dest.sin_addr,
- &his_addr.sin_addr,
- sizeof(data_dest.sin_addr)))
- {
+ if (paranoid &&
+ ((ntohs(data_dest.sin_port) <
+ IPPORT_RESERVED) ||
+ memcmp(&data_dest.sin_addr,
+ &his_addr.sin_addr,
+ sizeof(data_dest.sin_addr)))) {
usedefault = 1;
reply(500,
"Illegal PORT range rejected.");
- } else
-#endif
- {
+ } else {
usedefault = 0;
if (pdata >= 0) {
(void) close(pdata);
@@ -510,8 +508,9 @@ cmd
struct tm *t;
t = gmtime(&stbuf.st_mtime);
reply(213,
- "19%02d%02d%02d%02d%02d%02d",
- t->tm_year, t->tm_mon+1, t->tm_mday,
+ "%04d%02d%02d%02d%02d%02d",
+ 1900 + t->tm_year,
+ t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
}
}
@@ -572,11 +571,12 @@ host_port
{
char *a, *p;
- a = (char *)&data_dest.sin_addr;
- a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ data_dest.sin_len = sizeof(struct sockaddr_in);
+ data_dest.sin_family = AF_INET;
p = (char *)&data_dest.sin_port;
p[0] = $9; p[1] = $11;
- data_dest.sin_family = AF_INET;
+ a = (char *)&data_dest.sin_addr;
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
}
;
diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8
index 8cd6902..195a154 100644
--- a/libexec/ftpd/ftpd.8
+++ b/libexec/ftpd/ftpd.8
@@ -42,6 +42,7 @@ Internet File Transfer Protocol server
.Nm ftpd
.Op Fl dl
.Op Fl D
+.Op Fl R
.Op Fl S
.Op Fl U
.Op Fl T Ar maxtimeout
@@ -79,6 +80,17 @@ starting
from
.Xr inetd 8
and is thus useful on busy servers to reduce load.
+.It Fl R
+With this option set,
+.Nm ftpd
+will revert to historical behavior with regard to security checks on
+user operations and restrictions on PORT requests.
+Currently,
+.Nm ftpd
+will only honor PORT commands directed to unprivileged ports on the
+remote user's host (which violates the FTP protocol specification but
+closes some security holes).
+.
.It Fl S
With this option set,
.Nm ftpd
@@ -209,15 +221,23 @@ This allows users to utilize the metacharacters
.Dq Li \&*?[]{}~ .
.Pp
.Nm Ftpd
-authenticates users according to three rules.
+authenticates users according to five rules.
.Pp
.Bl -enum -offset indent
.It
The login name must be in the password data base,
-.Pa /etc/passwd ,
+.Pa /etc/pwd.db ,
and not have a null password.
In this case a password must be provided by the client before any
file operations may be performed.
+If the user has an S/Key key, the response from a successful USER
+command will include an S/Key challenge. The client may choose to respond
+with a PASS command giving either a standard password or an S/Key
+one-time password. The server will automatically determine which type of
+password it has been given and attempt to authenticate accordingly. See
+.Xr key 1
+for more information on S/Key authentication. S/Key is a Trademark of
+Bellcore.
.It
The login name must not appear in the file
.Pa /etc/ftpusers .
@@ -225,6 +245,19 @@ The login name must not appear in the file
The user must have a standard shell returned by
.Xr getusershell 3 .
.It
+If the user name appears in the file
+.Pa /etc/ftpchroot
+the session's root will be changed to the user's login directory by
+.Xr chroot 2
+as for an
+.Dq anonymous
+or
+.Dq ftp
+account (see next item). 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. The account should also be set up as for an
+anonymous account.
+.It
If the user name is
.Dq anonymous
or
@@ -235,7 +268,8 @@ file (user
.Dq ftp ) .
In this case the user is allowed
to log in by specifying any password (by convention an email address for
-the user should be used as the password). When the
+the user should be used as the password).
+When the
.Fl S
option is set, all transfers are logged as well.
.El
@@ -269,8 +303,8 @@ This program should be mode 111.
Make this directory owned by
.Dq root
and unwritable by anyone (mode 555).
-The files
-.Xr passwd 5
+The files pwd.db (see
+.Xr passwd 5 )
and
.Xr group 5
must be present for the
@@ -294,6 +328,8 @@ account in this directory.
.Bl -tag -width /etc/ftpwelcome -compact
.It Pa /etc/ftpusers
List of unwelcome/restricted users.
+.It Pa /etc/ftpchroot
+List of normal users who should be chroot'd.
.It Pa /etc/ftpwelcome
Welcome notice.
.It Pa /etc/ftpmotd
@@ -305,6 +341,7 @@ Log file for anonymous transfers.
.El
.Sh SEE ALSO
.Xr ftp 1 ,
+.Xr key 1 ,
.Xr getusershell 3 ,
.Xr inetd 8 ,
.Xr syslogd 8
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
index cb4b125..8cf26e4 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.17 1996/05/31 03:10:25 peter Exp $
+ * $Id: ftpd.c,v 1.18 1996/08/04 22:40:35 pst Exp $
*/
#ifndef lint
@@ -117,11 +117,11 @@ int timeout = 900; /* timeout after 15 minutes of inactivity */
int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
int logging;
int restricted_data_ports = 1;
+int paranoid = 1; /* be extra careful about security */
int guest;
-#ifdef STATS
+int dochroot;
int stats;
int statfd = -1;
-#endif
int type;
int form;
int stru; /* avoid C keyword */
@@ -139,8 +139,14 @@ int defumask = CMASK; /* default umask value */
char tmpline[7];
char hostname[MAXHOSTNAMELEN];
char remotehost[MAXHOSTNAMELEN];
-#ifdef STATS
char *ident = NULL;
+
+static char ttyline[20];
+char *tty = ttyline; /* for klogin */
+
+#if defined(KERBEROS)
+int notickets = 1;
+char *krbtkfile_env = NULL;
#endif
/*
@@ -188,7 +194,7 @@ char addr_string[20]; /* XXX */
static void ack __P((char *));
static void myoob __P((int));
-static int checkuser __P((char *));
+static int checkuser __P((char *, char *));
static FILE *dataconn __P((char *, off_t, char *));
static void dolog __P((struct sockaddr_in *));
static char *curdir __P((void));
@@ -202,9 +208,7 @@ static struct passwd *
sgetpwnam __P((char *));
static char *sgetsave __P((char *));
static void reapchild __P((int));
-#ifdef STATS
static void logxfer __P((char *, long, long));
-#endif
static char *
curdir()
@@ -242,44 +246,44 @@ main(argc, argv, envp)
#endif /* OLD_SETPROCTITLE */
-#ifdef STATS
- while ((ch = getopt(argc, argv, "dlDSt:T:u:v")) != EOF) {
-#else
- while ((ch = getopt(argc, argv, "dlDUt:T:u:v")) != EOF) {
-#endif
+ while ((ch = getopt(argc, argv, "dlDSUt:T:u:v")) != EOF) {
switch (ch) {
case 'D':
daemon_mode++;
break;
case 'd':
- debug = 1;
+ debug++;
break;
case 'l':
logging++; /* > 1 == extra logging */
break;
- case 'U':
- restricted_data_ports = 0;
+ case 'R':
+ paranoid = 0;
break;
- case 't':
- timeout = atoi(optarg);
- if (maxtimeout < timeout)
- maxtimeout = timeout;
- break;
-#ifdef STATS
case 'S':
- stats = 1;
+ stats++;
break;
-#endif
+
case 'T':
maxtimeout = atoi(optarg);
if (timeout > maxtimeout)
timeout = maxtimeout;
break;
+ case 't':
+ timeout = atoi(optarg);
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ break;
+
+ case 'U':
+ restricted_data_ports = 0;
+ break;
+
case 'u':
{
long val = 0;
@@ -397,6 +401,9 @@ main(argc, argv, envp)
#endif
data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+ /* set this here so klogin can use it... */
+ (void)sprintf(ttyline, "ftp%d", getpid());
+
/* Try to handle urgent data inline */
#ifdef SO_OOBINLINE
if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
@@ -458,8 +465,6 @@ lostconn(signo)
dologout(-1);
}
-static char ttyline[20];
-
/*
* Helper function for sgetpwnam().
*/
@@ -533,13 +538,17 @@ user(name)
if (guest) {
reply(530, "Can't change user from guest login.");
return;
+ } else if (dochroot) {
+ reply(530, "Can't change user from chroot user.");
+ return;
}
end_login();
}
guest = 0;
if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
- if (checkuser("ftp") || checkuser("anonymous"))
+ if (checkuser(_PATH_FTPUSERS, "ftp") ||
+ checkuser(_PATH_FTPUSERS, "anonymous"))
reply(530, "User %s access denied.", name);
else if ((pw = sgetpwnam("ftp")) != NULL) {
guest = 1;
@@ -561,7 +570,7 @@ user(name)
break;
endusershell();
- if (cp == NULL || checkuser(name)) {
+ if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) {
reply(530, "User %s access denied.", name);
if (logging)
syslog(LOG_NOTICE,
@@ -589,17 +598,18 @@ user(name)
}
/*
- * Check if a user is in the file _PATH_FTPUSERS
+ * Check if a user is in the file "fname"
*/
static int
-checkuser(name)
+checkuser(fname, name)
+ char *fname;
char *name;
{
FILE *fd;
int found = 0;
char *p, line[BUFSIZ];
- if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+ if ((fd = fopen(fname, "r")) != NULL) {
while (fgets(line, sizeof(line), fd) != NULL)
if ((p = strchr(line, '\n')) != NULL) {
*p = '\0';
@@ -629,13 +639,14 @@ end_login()
pw = NULL;
logged_in = 0;
guest = 0;
+ dochroot = 0;
}
void
pass(passwd)
char *passwd;
{
- char *salt, *xpasswd;
+ int rval;
FILE *fd;
static char homedir[MAXPATHLEN];
@@ -645,20 +656,33 @@ pass(passwd)
}
askpasswd = 0;
if (!guest) { /* "ftp" is only account allowed no password */
- if (pw == NULL)
- salt = "xx";
- else
- salt = pw->pw_passwd;
+ if (pw == NULL) {
+ rval = 1; /* failure below */
+ goto skip;
+ }
+#if defined(KERBEROS)
+ rval = klogin(pw, "", hostname, passwd);
+ if (rval == 0)
+ goto skip;
+#endif
#ifdef SKEY
- xpasswd = skey_crypt(passwd, salt, pw, pwok);
+ rval = strcmp(skey_crypt(passwd, pw->pw_passwd, pw, pwok),
+ passwd);
pwok = 0;
#else
- xpasswd = crypt(passwd, salt);
+ rval = strcmp(crypt(passwd, pw->passwd), passwd);
#endif
/* The strcmp does not catch null passwords! */
- if (pw == NULL || *pw->pw_passwd == '\0' ||
- (pw->pw_expire && time(NULL) >= pw->pw_expire) ||
- strcmp(xpasswd, pw->pw_passwd)) {
+ if (*pw->pw_passwd == '\0' ||
+ (pw->pw_expire && time(NULL) >= pw->pw_expire))
+ rval = 1; /* failure */
+skip:
+ /*
+ * If rval == 1, the user failed the authentication check
+ * above. If rval == 0, either Kerberos or local authentication
+ * succeeded.
+ */
+ if (rval) {
reply(530, "Login incorrect.");
if (logging)
syslog(LOG_NOTICE,
@@ -682,16 +706,14 @@ pass(passwd)
(void) initgroups(pw->pw_name, pw->pw_gid);
/* open wtmp before chroot */
- (void)sprintf(ttyline, "ftp%d", getpid());
logwtmp(ttyline, pw->pw_name, remotehost);
logged_in = 1;
-#ifdef STATS
- if (guest && stats == 1 && statfd < 0)
+ if (guest && stats && statfd < 0)
if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
stats = 0;
-#endif
+ dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
if (guest) {
/*
* We MUST do a chdir() after the chroot. Otherwise
@@ -702,6 +724,11 @@ pass(passwd)
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.");
+ goto bad;
+ }
} else if (chdir(pw->pw_dir) < 0) {
if (chdir("/") < 0) {
reply(530, "User %s: can't change directory to %s.",
@@ -737,16 +764,12 @@ pass(passwd)
(void) fclose(fd);
}
if (guest) {
-#ifdef STATS
- char * copy();
-
if (ident != NULL)
free(ident);
ident = strdup(passwd);
if (ident == NULL)
fatal("Ran out of memory.");
-#endif
reply(230, "Guest login ok, access restrictions apply.");
#ifdef SETPROCTITLE
snprintf(proctitle, sizeof(proctitle),
@@ -783,9 +806,7 @@ retrieve(cmd, name)
FILE *fin, *dout;
struct stat st;
int (*closefunc) __P((FILE *));
-#ifdef STATS
long start;
-#endif
if (cmd == 0) {
fin = fopen(name, "r"), closefunc = fclose;
@@ -835,15 +856,11 @@ retrieve(cmd, name)
dout = dataconn(name, st.st_size, "w");
if (dout == NULL)
goto done;
-#ifdef STATS
time(&start);
-#endif
send_data(fin, dout, st.st_blksize, st.st_size,
restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
-#ifdef STATS
if (cmd == 0 && guest && stats)
logxfer(name, st.st_size, start);
-#endif
(void) fclose(dout);
data = -1;
pdata = -1;
@@ -941,6 +958,7 @@ getdatasock(mode)
(char *) &on, sizeof(on)) < 0)
goto bad;
/* anchor socket to avoid multi-homing problems */
+ data_source.sin_len = sizeof(struct sockaddr_in);
data_source.sin_family = AF_INET;
data_source.sin_addr = ctrl_addr.sin_addr;
for (tries = 1; ; tries++) {
@@ -1022,7 +1040,7 @@ dataconn(name, size, mode)
(void) close(pdata);
pdata = s;
#ifdef IP_TOS
- tos = IPTOS_LOWDELAY;
+ tos = IPTOS_THROUGHPUT;
(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
sizeof(int));
#endif
@@ -1578,6 +1596,10 @@ dologout(status)
if (logged_in) {
(void) seteuid((uid_t)0);
logwtmp(ttyline, "", "");
+#if defined(KERBEROS)
+ if (!notickets && krbtkfile_env)
+ unlink(krbtkfile_env);
+#endif
}
/* beware of flushing buffers after a SIGPIPE */
_exit(status);
@@ -1906,7 +1928,6 @@ setproctitle(fmt, va_alist)
}
#endif /* OLD_SETPROCTITLE */
-#ifdef STATS
static void
logxfer(name, size, start)
char *name;
@@ -1925,4 +1946,3 @@ logxfer(name, size, start)
write(statfd, buf, strlen(buf));
}
}
-#endif
diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h
index 85a656b..02278e7 100644
--- a/libexec/ftpd/pathnames.h
+++ b/libexec/ftpd/pathnames.h
@@ -35,6 +35,7 @@
#include <paths.h>
+#define _PATH_FTPCHROOT "/etc/ftpchroot"
#define _PATH_FTPWELCOME "/etc/ftpwelcome"
#define _PATH_FTPLOGINMESG "/etc/ftpmotd"
#define _PATH_FTPDSTATFILE "/var/log/ftpd"
OpenPOWER on IntegriCloud