-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Sup all? In light of the recent WU-ftpd exploits and general security concearns, I decided to change to a different ftpd.(duh) The largest feature about wu-ftpd that I needed was the ability to specify the passive port range so as to be able to write firewall rulesets with default deny and only a small range of open ports for passive ftp. I decided to integrate the suppport for that into freebsd's native ftpd so that I could use an ftpd that I had faith in. So here it is. These patches are against a brand new -stable so they should work well. Flames, Praise, Wine are all welcome ;) (nothing like the napa valley or a good aussie red) I even updated the man page for easy integration. If somebody commits this, please tell me... thanks Visigoth Damieon Stark Sr. Unix Systems Administrator [EMAIL PROTECTED] PGP Public Key: www.telemere.net/~visigoth/visigoth.asc ____________________________________________________________________________ | M$ -Where do you want to go today? | Linux -Where do you want to go tomorrow?| FreeBSD - The POWER to serve Freebsd -Are you guys comming or what? | http://www.freebsd.org | | - ---------------------------------------------------------------------------- -----BEGIN PGP SIGNATURE----- Version: PGPfreeware 5.0i for non-commercial use Charset: noconv iQA/AwUBOVocbjnmC/+RTnGeEQLFUgCg5sKoRD2gj7P+hIssj4zujPgdd/IAoLPk xosQMqmtehAjA6sQb8/DjTI7 =zbKS -----END PGP SIGNATURE-----
--- /usr/src/libexec/ftpd/ftpd.c.old Wed Jun 28 10:08:42 2000 +++ /usr/src/libexec/ftpd/ftpd.c Wed Jun 28 09:49:05 2000 @@ -125,8 +125,9 @@ union sockunion his_addr; union sockunion pasv_addr; int daemon_mode; +int pass_port_opt,min_pport,max_pport; int data; jmp_buf errcatch, urgcatch; int logged_in; struct passwd *pw; @@ -158,8 +159,9 @@ char tmpline[7]; char *hostname; #ifdef VIRTUAL_HOSTING char *ftpuser; +char *opt_ptr; int epsvall = 0; static struct ftphost { @@ -289,9 +291,9 @@ LastArgv = envp[-1] + strlen(envp[-1]); #endif /* OLD_SETPROCTITLE */ - while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:46")) != -1) { + while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:P:46")) != -1) { switch (ch) { case 'D': daemon_mode++; break; @@ -335,8 +337,21 @@ case 'p': pid_file = optarg; break; + case 'P': + pass_port_opt++; + if((min_pport = strtod(optarg, &opt_ptr )) == 0 ) + errx(1,"Unrecognized passive port number!\n"); + + opt_ptr++; + if((max_pport = strtod(opt_ptr,NULL)) == 0 ) + errx(1,"Unrecognized passive port number!\n"); + + if( min_pport > max_pport ) + errx(1,"Portrange from %d to %d +invalid!\n",min_pport,max_pport); + break; + case 'u': { long val = 0; @@ -2323,9 +2338,9 @@ */ void passive() { - int len; + int len,next; char *p, *a; if (pdata >= 0) /* close old port if one set */ close(pdata); @@ -2337,33 +2352,57 @@ } (void) seteuid((uid_t)0); + if (pass_port_opt < 1) { /* if passive ports are in use don't bother + with IP[V6]_PORTRANGE. Doesn't affect port + asignment, but saves little cpu? ;) */ + #ifdef IP_PORTRANGE - if (ctrl_addr.su_family == AF_INET) { - int on = restricted_data_ports ? IP_PORTRANGE_HIGH + if (ctrl_addr.su_family == AF_INET) { + int on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; - if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, + if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error; - } + } #endif #ifdef IPV6_PORTRANGE - if (ctrl_addr.su_family == AF_INET6) { - int on = restricted_data_ports ? IPV6_PORTRANGE_HIGH + if (ctrl_addr.su_family == AF_INET6) { + int on = restricted_data_ports ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; - if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, + if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error; - } + } #endif - + } pasv_addr = ctrl_addr; - pasv_addr.su_port = 0; - if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) + + if (pass_port_opt) { + for(next = min_pport; next <= max_pport; next++) { + pasv_addr.su_port = htons(next); + if (bind(pdata, (struct sockaddr *)&pasv_addr, + pasv_addr.su_len) < 0 ) { + if (errno == EADDRINUSE ) + continue; /* try next available passive port */ + else + goto pasv_error; /* error other than EADDRINUSE! */ + } + break; /* bind() success */ + } + if ( next > max_pport ) + goto pasv_error; /* unsuccessful at getting pasv port */ + + } else { /* not specifying passive port range */ + + + pasv_addr.su_port = 0; + if (bind(pdata, (struct sockaddr *)&pasv_addr,pasv_addr.su_len) < 0) goto pasv_error; + } (void) seteuid((uid_t)pw->pw_uid); len = sizeof(pasv_addr); @@ -2405,9 +2444,9 @@ long_passive(cmd, pf) char *cmd; int pf; { - int len; + int len,next; char *p, *a; if (pdata >= 0) /* close old port if one set */ close(pdata); @@ -2446,35 +2485,59 @@ } (void) seteuid((uid_t)0); - pasv_addr = ctrl_addr; - pasv_addr.su_port = 0; - len = pasv_addr.su_len; + if (pass_port_opt < 1) { /* if passive ports are in use don't bother + with IP_PORTRANGE. Doesn't affect port + asignment, but saves little cpu? ;) */ #ifdef IP_PORTRANGE - if (ctrl_addr.su_family == AF_INET) { - int on = restricted_data_ports ? IP_PORTRANGE_HIGH + if (ctrl_addr.su_family == AF_INET) { + int on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; - if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, + if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error; - } + } #endif #ifdef IPV6_PORTRANGE - if (ctrl_addr.su_family == AF_INET6) { - int on = restricted_data_ports ? IPV6_PORTRANGE_HIGH + if (ctrl_addr.su_family == AF_INET6) { + int on = restricted_data_ports ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; - if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, + if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error; - } + } #endif + } + + pasv_addr = ctrl_addr; + len = pasv_addr.su_len; + + if (pass_port_opt) { + for(next = min_pport; next <= max_pport; next++) { + pasv_addr.su_port = htons(next); + if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0 ) { + if (errno == EADDRINUSE ) + continue; /* try next available passive port */ + else + goto pasv_error; /* error other than EADDRINUSE! */ + } + break; /* bind() success */ + } + if ( next > max_pport ) + goto pasv_error; /* unsuccessful at getting pasv port */ + + } else { /* not specifying passive port range */ - if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) + + pasv_addr.su_port = 0; + if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) goto pasv_error; + + } (void) seteuid((uid_t)pw->pw_uid); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
--- /usr/src/libexec/ftpd/ftpd.8.old Wed Jun 28 10:11:14 2000 +++ /usr/src/libexec/ftpd/ftpd.8 Wed Jun 28 10:21:39 2000 @@ -53,8 +53,9 @@ .Op Fl T Ar maxtimeout .Op Fl t Ar timeout .Op Fl a Ar address .Op Fl p Ar file +.Op Fl P Ar minport-maxport .Sh DESCRIPTION .Nm Ftpd is the Internet File Transfer Protocol @@ -122,8 +123,17 @@ seconds with the .Fl T option. The default limit is 2 hours. +.It Fl P +With this option set +.Nm +allows the administrator to specify a range of ports +.Ar minport-maxport +to be used for +incomming passive data connections. This allows a default deny firewall +on an ftp server to just allow a range of ports, and tightens up security in +general. .It Fl t The inactivity timeout period is set to .Ar timeout seconds (the default is 15 minutes).