diff options
author | pst <pst@FreeBSD.org> | 1996-08-05 00:21:15 +0000 |
---|---|---|
committer | pst <pst@FreeBSD.org> | 1996-08-05 00:21:15 +0000 |
commit | fca5bc38ec00d8cb5087036679424e8817d2b782 (patch) | |
tree | 12d2de9b0d4e2a49a22dcb35f943c6b5f2e301fb /libexec/ftpd | |
parent | 1d37bd800534129c143f350649cec9fa9248e26b (diff) | |
download | FreeBSD-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/Makefile | 9 | ||||
-rw-r--r-- | libexec/ftpd/ftpcmd.y | 30 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.8 | 47 | ||||
-rw-r--r-- | libexec/ftpd/ftpd.c | 134 | ||||
-rw-r--r-- | libexec/ftpd/pathnames.h | 1 |
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" |