diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index adcc9198f807..a21838081502 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -5,6 +5,7 @@ MAN8= ftpd.8 SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c CFLAGS+=-DSETPROCTITLE -DSKEY -DSTATS +CFLAGS+=-DFTP_DATA_BOTTOM=40000 -DFTP_DATA_TOP=44999 LDADD= -lskey -lmd -lcrypt DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 2471961c58e4..6e5b199cbfd3 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 S +.Op Fl U .Op Fl T Ar maxtimeout .Op Fl t Ar timeout .Sh DESCRIPTION @@ -74,6 +75,13 @@ logs all anonymous transfers to the file .Pa /var/log/ftpd when this file exists. . +.It Fl U +In previous versions of +.Nm ftpd , +when a passive mode client requested a data connection to the server, +the server would use data ports in the range 1024..4999. Now, by default, +the server will use data ports in the range 40000..44999. Specifying this +option will revert to the old behavior. .It Fl T A client may also request a different timeout period; the maximum period allowed may be set to diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index f6fc3795f5ef..8776124171c5 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.9 1995/05/22 11:03:55 davidg Exp $ + * $Id: ftpd.c,v 1.10 1995/05/30 05:45:58 rgrimes Exp $ */ #ifndef lint @@ -113,6 +113,7 @@ int debug; 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 guest; #ifdef STATS int stats; @@ -260,7 +261,7 @@ main(argc, argv, envp) #ifdef STATS while ((ch = getopt(argc, argv, "dlSt:T:u:v")) != EOF) { #else - while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) { + while ((ch = getopt(argc, argv, "dlUt:T:u:v")) != EOF) { #endif switch (ch) { case 'd': @@ -271,6 +272,10 @@ main(argc, argv, envp) logging++; /* > 1 == extra logging */ break; + case 'U': + restricted_data_ports = 0; + break; + case 't': timeout = atoi(optarg); if (maxtimeout < timeout) @@ -1518,6 +1523,7 @@ void passive() { int len; + u_short port; char *p, *a; pdata = socket(AF_INET, SOCK_STREAM, 0); @@ -1525,14 +1531,37 @@ passive() perror_reply(425, "Can't open passive connection"); return; } - pasv_addr = ctrl_addr; - pasv_addr.sin_port = 0; - (void) seteuid((uid_t)0); - if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { + + if (restricted_data_ports) { + for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) { + pasv_addr = ctrl_addr; + pasv_addr.sin_port = htons(port); + (void) seteuid((uid_t)0); + if (bind(pdata, (struct sockaddr *)&pasv_addr, + sizeof(pasv_addr)) < 0) { + (void) seteuid((uid_t)pw->pw_uid); + if (errno == EADDRINUSE) + continue; + else + goto pasv_error; + } + (void) seteuid((uid_t)pw->pw_uid); + break; + } + if (port > FTP_DATA_TOP) + goto pasv_error; + } else { + pasv_addr = ctrl_addr; + pasv_addr.sin_port = 0; + (void) seteuid((uid_t)0); + if (bind(pdata, (struct sockaddr *)&pasv_addr, + sizeof(pasv_addr)) < 0) { + (void) seteuid((uid_t)pw->pw_uid); + goto pasv_error; + } (void) seteuid((uid_t)pw->pw_uid); - goto pasv_error; } - (void) seteuid((uid_t)pw->pw_uid); + len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile index dc8197fb2b68..e9d0a6248c0e 100644 --- a/usr.bin/ftp/Makefile +++ b/usr.bin/ftp/Makefile @@ -4,5 +4,6 @@ PROG= ftp SRCS= cmds.c cmdtab.c ftp.c main.c ruserpass.c domacro.c LINKS= ${BINDIR}/ftp ${BINDIR}/pftp MLINKS= ftp.1 pftp.1 +CFLAGS+=-DFTP_DATA_BOTTOM=40000 -DFTP_DATA_TOP=44999 .include diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c index 34cca4ce55e1..9756042b5023 100644 --- a/usr.bin/ftp/cmds.c +++ b/usr.bin/ftp/cmds.c @@ -2143,6 +2143,18 @@ setpassive() code = passivemode; } +/* + * Restrict FTP data port range to a high group of "safe" ports + */ +void +setrestrict() +{ + restricted_data_ports = !restricted_data_ports; + printf("Data port range restrictions %s.\n", + onoff(restricted_data_ports)); + code = restricted_data_ports; +} + /* * get size of file on remote machine */ diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c index a55cd86eb0ea..fd9afb22f58c 100644 --- a/usr.bin/ftp/cmdtab.c +++ b/usr.bin/ftp/cmdtab.c @@ -90,6 +90,7 @@ char regethelp[] = "get file restarting at end of local file"; char remotehelp[] = "get help from remote server"; char renamehelp[] = "rename file"; char restarthelp[]= "restart file transfer at bytecount"; +char restricthelp[]= "toggle restriction of data port range"; char rmdirhelp[] = "remove directory on the remote machine"; char rmtstatushelp[]="show status of remote machine"; char runiquehelp[] = "toggle store unique for local files"; @@ -166,6 +167,7 @@ struct cmd cmdtab[] = { { "rename", renamehelp, 0, 1, 1, renamefile }, { "reset", resethelp, 0, 1, 1, reset }, { "restart", restarthelp, 1, 1, 1, restart }, + { "restrict", restricthelp, 0, 0, 0, setrestrict }, { "rmdir", rmdirhelp, 0, 1, 1, removedir }, { "runique", runiquehelp, 0, 0, 1, setrunique }, { "send", sendhelp, 1, 1, 1, put }, diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h index 30ba06bdb049..fb9aecf2d1de 100644 --- a/usr.bin/ftp/extern.h +++ b/usr.bin/ftp/extern.h @@ -124,6 +124,7 @@ void setpassive __P(()); void setpeer __P((int, char **)); void setport __P((int, char **)); void setprompt __P((int, char **)); +void setrestrict __P(()); void setrunique __P((int, char **)); void setstruct __P((int, char **)); void setsunique __P((int, char **)); diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 index 47904077b2c4..4a3df6aa246b 100644 --- a/usr.bin/ftp/ftp.1 +++ b/usr.bin/ftp/ftp.1 @@ -45,6 +45,7 @@ file transfer program .Op Fl d .Op Fl i .Op Fl n +.Op Fl U .Op Fl p .Op Fl g .Op Ar host @@ -86,6 +87,8 @@ multiple file transfers. Enables debugging. .It Fl g Disables file name globbing. +.It Fl U +Disable data port range restrictions. .It Fl p Enable passive mode operation for use behind connection filtering firewalls. .El @@ -733,6 +736,18 @@ On .Ux systems, marker is usually a byte offset into the file. +.It Ic restrict +Toggle data port range restrictions. +When not operating in passive mode, the +.Nm ftp , +client program requests that the remote server open a connection back +to the client host on a separate data port. In previous versions, that +remote port fell in the range 1024..4999. However, most firewall setups +filter that range of TCP ports because other services reside there. +The default behavior now is for the client to request that the server +connect back to the client using the port range 40000..44999. Firewall +administrators can chose to allow TCP connections in that range, if they +deem it to not be a security risk. .It Ic rmdir Ar directory-name Delete a directory on the remote machine. .It Ic runique diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c index 10327c58248a..137e8c2198b6 100644 --- a/usr.bin/ftp/ftp.c +++ b/usr.bin/ftp/ftp.c @@ -1000,7 +1000,9 @@ initconn() char *p, *a; int result, len, tmpno = 0; int on = 1; + int count; u_long a1,a2,a3,a4,p1,p2; + static u_short last_port = FTP_DATA_BOTTOM; if (passivemode) { data = socket(AF_INET, SOCK_STREAM, 0); @@ -1051,9 +1053,6 @@ initconn() } noport: - data_addr = myctladdr; - if (sendport) - data_addr.sin_port = 0; /* let system pick one */ if (data != -1) (void) close(data); data = socket(AF_INET, SOCK_STREAM, 0); @@ -1063,15 +1062,53 @@ initconn() sendport = 1; return (1); } - if (!sendport) - if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { + data_addr = myctladdr; + if (sendport) { + if (restricted_data_ports) { + for (count = 0; + count < FTP_DATA_TOP-FTP_DATA_BOTTOM; count++) { + last_port++; + if (last_port < FTP_DATA_BOTTOM || + last_port > FTP_DATA_TOP) + last_port = FTP_DATA_BOTTOM; + + data_addr.sin_port = htons(last_port); + if (bind(data, (struct sockaddr *)&data_addr, + sizeof(data_addr)) < 0) { + if (errno == EADDRINUSE) + continue; + else { + warn("bind"); + goto bad; + } + } + break; + } + if (count >= FTP_DATA_TOP-FTP_DATA_BOTTOM) { + perror("ftp: all data ports in use"); + goto bad; + } + } else { + data_addr.sin_port = 0; /* use any port */ + if (bind(data, (struct sockaddr *)&data_addr, + sizeof(data_addr)) < 0) { + warn("bind"); + goto bad; + } + } + } else { + if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof (on)) < 0) { warn("setsockopt (reuse address)"); goto bad; } - if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) { - warn("bind"); - goto bad; - } + if (bind(data, (struct sockaddr *)&data_addr, + sizeof (data_addr)) < 0) { + warn("bind"); + goto bad; + } + } + if (options & SO_DEBUG && setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) warn("setsockopt (ignored)"); diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h index 5778f6978d72..85895a8ae2ef 100644 --- a/usr.bin/ftp/ftp_var.h +++ b/usr.bin/ftp/ftp_var.h @@ -67,6 +67,7 @@ int code; /* return/reply code for ftp command */ int crflag; /* if 1, strip car. rets. on ascii gets */ char pasv[64]; /* passive port for proxy data connection */ int passivemode; /* passive mode enabled */ +int restricted_data_ports; /* restrict data port range */ char *altarg; /* argv[1] with no shell-like preprocessing */ char ntin[17]; /* input translation table */ char ntout[17]; /* output translation table */ diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c index 7df165ca5882..cca7a9193717 100644 --- a/usr.bin/ftp/main.c +++ b/usr.bin/ftp/main.c @@ -82,13 +82,14 @@ main(argc, argv) interactive = 1; autologin = 1; passivemode = 0; + restricted_data_ports = 1; cp = strrchr(argv[0], '/'); cp = (cp == NULL) ? argv[0] : cp+1; if (strcmp(cp, "pftp") == 0) passivemode = 1; - while ((ch = getopt(argc, argv, "dginptv")) != EOF) { + while ((ch = getopt(argc, argv, "dginptvU")) != EOF) { switch (ch) { case 'd': options |= SO_DEBUG; @@ -119,6 +120,10 @@ main(argc, argv) verbose++; break; + case 'U': + restricted_data_ports = 0; + break; + default: (void)fprintf(stderr, "usage: ftp [-dginptv] [host [port]]\n");