diff options
Diffstat (limited to 'etc/inc')
-rw-r--r-- | etc/inc/captiveportal.inc | 642 | ||||
-rw-r--r-- | etc/inc/config.inc | 551 | ||||
-rw-r--r-- | etc/inc/filter.inc | 946 | ||||
-rw-r--r-- | etc/inc/functions.inc | 41 | ||||
-rw-r--r-- | etc/inc/globals.inc | 54 | ||||
-rw-r--r-- | etc/inc/interfaces.inc | 740 | ||||
-rw-r--r-- | etc/inc/openvpn.inc | 559 | ||||
-rw-r--r-- | etc/inc/services.inc | 440 | ||||
-rw-r--r-- | etc/inc/shaper.inc | 403 | ||||
-rw-r--r-- | etc/inc/system.inc | 563 | ||||
-rw-r--r-- | etc/inc/util.inc | 421 | ||||
-rw-r--r-- | etc/inc/vpn.inc | 559 | ||||
-rw-r--r-- | etc/inc/xmlparse.inc | 205 |
13 files changed, 6124 insertions, 0 deletions
diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc new file mode 100644 index 0000000..d5d78b1 --- /dev/null +++ b/etc/inc/captiveportal.inc @@ -0,0 +1,642 @@ +<?php +/* + captiveportal.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); +require_once("radius_accounting.inc") ; + +function captiveportal_configure() { + global $config, $g; + + if (isset($config['captiveportal']['enable']) && + (($config['captiveportal']['interface'] == "lan") || + isset($config['interfaces'][$config['captiveportal']['interface']]['enable']))) { + + if ($g['booting']) + echo "Starting captive portal... "; + + /* kill any running mini_httpd */ + killbypid("{$g['varrun_path']}/mini_httpd.cp.pid"); + killbypid("{$g['varrun_path']}/mini_httpd.cps.pid"); + + /* kill any running minicron */ + killbypid("{$g['varrun_path']}/minicron.pid"); + + /* generate ipfw rules */ + $cprules = captiveportal_rules_generate(); + + /* make sure ipfw is loaded */ + mwexec("/sbin/kldload ipfw"); + + /* stop accounting on all clients */ + captiveportal_radius_stop_all() ; + + /* remove old information */ + unlink_if_exists("{$g['vardb_path']}/captiveportal.nextrule"); + unlink_if_exists("{$g['vardb_path']}/captiveportal.db"); + unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db"); + unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db"); + unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db"); + + /* write portal page */ + if ($config['captiveportal']['page']['htmltext']) + $htmltext = base64_decode($config['captiveportal']['page']['htmltext']); + else { + /* example/template page */ + $htmltext = <<<EOD +<html> +<head> +<title>m0n0wall captive portal</title> +</head> +<body> +<h2>m0n0wall captive portal</h2> +<p>This is the default captive portal page. Please upload your own custom HTML file on the <em>Services: Captive portal</em> screen in the m0n0wall webGUI.</p> +<form method="post" action=""> + <input name="accept" type="submit" value="Continue"> +</form> +</body> +</html> + +EOD; + } + + $fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w"); + if ($fd) { + fwrite($fd, $htmltext); + fclose($fd); + } + + /* write error page */ + if ($config['captiveportal']['page']['errtext']) + $errtext = base64_decode($config['captiveportal']['page']['errtext']); + else { + /* example page */ + $errtext = <<<EOD +<html> +<head> +<title>Authentication error</title> +</head> +<body> +<font color="#cc0000"><h2>Authentication error</h2></font> +<b> +Username and/or password invalid. +<br><br> +<a href="javascript:history.back()">Go back</a> +</b> +</body> +</html> + +EOD; + } + + $fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w"); + if ($fd) { + fwrite($fd, $errtext); + fclose($fd); + } + + /* load rules */ + mwexec("/sbin/ipfw -f delete set 1"); + mwexec("/sbin/ipfw -f delete set 2"); + mwexec("/sbin/ipfw -f delete set 3"); + + /* XXX - seems like ipfw cannot accept rules directly on stdin, + so we have to write them to a temporary file first */ + $fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w"); + if (!$fd) { + printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n"); + return 1; + } + + fwrite($fd, $cprules); + fclose($fd); + + mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.cp.rules"); + + unlink("{$g['tmp_path']}/ipfw.cp.rules"); + + /* filter on layer2 as well so we can check MAC addresses */ + mwexec("/sbin/sysctl net.link.ether.ipfw=1"); + + chdir($g['captiveportal_path']); + + /* start web server */ + mwexec("/usr/local/sbin/mini_httpd -a -M 0 -u root -maxproc 16" . + " -p 8000 -i {$g['varrun_path']}/mini_httpd.cp.pid"); + + /* fire up another one for HTTPS if requested */ + if (isset($config['captiveportal']['httpslogin']) && + $config['captiveportal']['certificate'] && $config['captiveportal']['private-key']) { + + $cert = base64_decode($config['captiveportal']['certificate']); + $key = base64_decode($config['captiveportal']['private-key']); + + $fd = fopen("{$g['varetc_path']}/cert-portal.pem", "w"); + if (!$fd) { + printf("Error: cannot open cert-portal.pem in system_webgui_start().\n"); + return 1; + } + chmod("{$g['varetc_path']}/cert-portal.pem", 0600); + fwrite($fd, $cert); + fwrite($fd, "\n"); + fwrite($fd, $key); + fclose($fd); + + mwexec("/usr/local/sbin/mini_httpd -S -a -M 0 -E {$g['varetc_path']}/cert-portal.pem" . + " -u root -maxproc 16 -p 8001" . + " -i {$g['varrun_path']}/mini_httpd.cps.pid"); + } + + /* start pruning process (interval = 60 seconds) */ + mwexec("/usr/local/bin/minicron 60 {$g['varrun_path']}/minicron.pid " . + "/etc/rc.prunecaptiveportal"); + + /* generate passthru mac database */ + captiveportal_passthrumac_configure() ; + /* create allowed ip database and insert ipfw rules to make it so */ + captiveportal_allowedip_configure() ; + + /* generate radius server database */ + if($config['captiveportal']['radiusip']) { + $radiusip = $config['captiveportal']['radiusip'] ; + + if($config['captiveportal']['radiusport']) + $radiusport = $config['captiveportal']['radiusport'] ; + else + $radiusport = 1812; + + if($config['captiveportal']['radiusacctport']) + $radiusacctport = $config['captiveportal']['radiusacctport'] ; + else + $radiusacctport = 1813; + + $radiuskey = $config['captiveportal']['radiuskey']; + + $fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w"); + if (!$fd) { + printf("Error: cannot open radius DB file in captiveportal_configure().\n"); + return 1; + } else { + fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey) ; + } + fclose($fd) ; + } + + + if ($g['booting']) + echo "done\n"; + + } else { + killbypid("{$g['varrun_path']}/mini_httpd.cp.pid"); + killbypid("{$g['varrun_path']}/minicron.pid"); + captiveportal_radius_stop_all() ; + mwexec("/sbin/sysctl net.link.ether.ipfw=0"); + if (!isset($config['shaper']['enable'])) { + /* unload ipfw */ + mwexec("/sbin/kldunload ipfw"); + } else { + /* shaper is on - just remove our rules */ + mwexec("/sbin/ipfw -f delete set 1"); + mwexec("/sbin/ipfw -f delete set 2"); + mwexec("/sbin/ipfw -f delete set 3"); + } + } + + return 0; +} + +function captiveportal_rules_generate() { + global $config, $g; + + $cpifn = $config['captiveportal']['interface']; + $cpif = $config['interfaces'][$cpifn]['if']; + $cpip = $config['interfaces'][$cpifn]['ipaddr']; + + /* note: the captive portal daemon inserts all pass rules for authenticated + clients as skipto 50000 rules to make traffic shaping work */ + + $cprules = ""; + + /* captive portal on LAN interface? */ + if ($cpifn == "lan") { + /* add anti-lockout rules */ + $cprules .= <<<EOD +add 500 set 1 pass all from $cpip to any out via $cpif +add 501 set 1 pass all from any to $cpip in via $cpif + +EOD; + } + + $cprules .= <<<EOD +# skip to traffic shaper if not on captive portal interface +add 1000 set 1 skipto 50000 all from any to any not layer2 not via $cpif +# pass all layer2 traffic on other interfaces +add 1001 set 1 pass layer2 not via $cpif + +# layer 2: pass ARP +add 1100 set 1 pass layer2 mac-type arp +# layer 2: block anything else non-IP +add 1101 set 1 deny layer2 not mac-type ip +# layer 2: check if MAC addresses of authenticated clients are correct +add 1102 set 1 skipto 20000 layer2 + +# allow access to our DHCP server (which needs to be able to ping clients as well) +add 1200 set 1 pass udp from any 68 to 255.255.255.255 67 in +add 1201 set 1 pass udp from any 68 to $cpip 67 in +add 1202 set 1 pass udp from $cpip 67 to any 68 out +add 1203 set 1 pass icmp from $cpip to any out icmptype 8 +add 1204 set 1 pass icmp from any to $cpip in icmptype 0 + +# allow access to our DNS forwarder +add 1300 set 1 pass udp from any to $cpip 53 in +add 1301 set 1 pass udp from $cpip 53 to any out + +# allow access to our web server +add 1302 set 1 pass tcp from any to $cpip 8000 in +add 1303 set 1 pass tcp from $cpip 8000 to any out + +EOD; + + if (isset($config['captiveportal']['httpslogin'])) { + $cprules .= <<<EOD +add 1304 set 1 pass tcp from any to $cpip 8001 in +add 1305 set 1 pass tcp from $cpip 8001 to any out + +EOD; + } + + $cprules .= <<<EOD + +# ... 10000-19899: rules per authenticated client go here... + +# redirect non-authenticated clients to captive portal +add 19900 set 1 fwd 127.0.0.1,8000 tcp from any to any 80 in +# let the responses from the captive portal web server back out +add 19901 set 1 pass tcp from any 80 to any out +# block everything else +add 19902 set 1 deny all from any to any + +# ... 20000-29899: layer2 block rules per authenticated client go here... + +# pass everything else on layer2 +add 29900 set 1 pass all from any to any layer2 + +EOD; + + return $cprules; +} + +/* remove clients that have been around for longer than the specified amount of time */ +/* db file structure: timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid */ +function captiveportal_prune_old() { + + global $g, $config; + + /* check for expired entries */ + if ($config['captiveportal']['timeout']) + $timeout = $config['captiveportal']['timeout'] * 60; + else + $timeout = 0; + + if ($config['captiveportal']['idletimeout']) + $idletimeout = $config['captiveportal']['idletimeout'] * 60; + else + $idletimeout = 0; + + if (!$timeout && !$idletimeout) + return; + + captiveportal_lock(); + + /* read database */ + $cpdb = captiveportal_read_db(); + + $radiusservers = captiveportal_get_radius_servers(); + + for ($i = 0; $i < count($cpdb); $i++) { + + $timedout = false; + + /* hard timeout? */ + if ($timeout) { + if ((time() - $cpdb[$i][0]) >= $timeout) + $timedout = true; + } + + /* if an idle timeout is specified, get last activity timestamp from ipfw */ + if (!$timedout && $idletimeout) { + $lastact = captiveportal_get_last_activity($cpdb[$i][1]); + if ($lastact && ((time() - $lastact) >= $idletimeout)) + $timedout = true; + } + + if ($timedout) { + /* this client needs to be deleted - remove ipfw rules */ + if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) { + RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno + $cpdb[$i][4], // username + $cpdb[$i][5], // sessionid + $cpdb[$i][0], // start time + $radiusservers[0]['ipaddr'], + $radiusservers[0]['acctport'], + $radiusservers[0]['key']); + } + mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000)); + unset($cpdb[$i]); + } + } + + /* write database */ + captiveportal_write_db($cpdb); + + captiveportal_unlock(); +} + +/* remove a single client by ipfw rule number */ +function captiveportal_disconnect_client($id) { + + global $g, $config; + + captiveportal_lock(); + + /* read database */ + $cpdb = captiveportal_read_db(); + $radiusservers = captiveportal_get_radius_servers(); + + /* find entry */ + for ($i = 0; $i < count($cpdb); $i++) { + if ($cpdb[$i][1] == $id) { + /* this client needs to be deleted - remove ipfw rules */ + if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) { + RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno + $cpdb[$i][4], // username + $cpdb[$i][5], // sessionid + $cpdb[$i][0], // start time + $radiusservers[0]['ipaddr'], + $radiusservers[0]['acctport'], + $radiusservers[0]['key']); + } + mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000)); + unset($cpdb[$i]); + break; + } + } + + /* write database */ + captiveportal_write_db($cpdb); + + captiveportal_unlock(); +} + +/* send RADIUS acct stop for all current clients */ +function captiveportal_radius_stop_all() { + global $g, $config; + + captiveportal_lock() ; + $cpdb = captiveportal_read_db() ; + + $radiusservers = captiveportal_get_radius_servers(); + + if (isset($radiusservers[0])) { + for ($i = 0; $i < count($cpdb); $i++) { + RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno + $cpdb[$i][4], // username + $cpdb[$i][5], // sessionid + $cpdb[$i][0], // start time + $radiusservers[0]['ipaddr'], + $radiusservers[0]['acctport'], + $radiusservers[0]['key']); + } + } + captiveportal_unlock() ; +} + +function captiveportal_passthrumac_configure() { + global $config, $g; + + /* clear out passthru macs, if necessary */ + if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) { + unlink("{$g['vardb_path']}/captiveportal_mac.db"); + } + + if (is_array($config['captiveportal']['passthrumac'])) { + + $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w"); + if (!$fd) { + printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n"); + return 1; + } + + foreach ($config['captiveportal']['passthrumac'] as $macent) { + /* record passthru mac so it can be recognized and let thru */ + fwrite($fd, $macent['mac'] . "\n"); + } + + fclose($fd); + } + + return 0; +} + +function captiveportal_allowedip_configure() { + global $config, $g; + + captiveportal_lock() ; + + /* clear out existing allowed ips, if necessary */ + if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) { + $fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "r"); + if ($fd) { + while (!feof($fd)) { + $line = trim(fgets($fd)); + if($line) { + list($ip,$rule) = explode(",",$line); + mwexec("/sbin/ipfw delete $rule") ; + } + } + } + fclose($fd) ; + unlink("{$g['vardb_path']}/captiveportal_ip.db"); + } + + /* get next ipfw rule number */ + if (file_exists("{$g['vardb_path']}/captiveportal.nextrule")) + $ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule")); + if (!$ruleno) + $ruleno = 10000; /* first rule number */ + + if (is_array($config['captiveportal']['allowedip'])) { + + $fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w"); + if (!$fd) { + printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n"); + captiveportal_unlock() ; + return 1; + } + + foreach ($config['captiveportal']['allowedip'] as $ipent) { + /* record allowed ip so it can be recognized and removed later */ + fwrite($fd, $ipent['ip'] . "," . $ruleno ."\n"); + /* insert ipfw rule to allow ip thru */ + if($ipent['dir'] == "from") { + mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from ".$ipent['ip']." to any in") ; + mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to ".$ipent['ip']." out") ; + } else { + mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to ".$ipent['ip']." in") ; + mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from ".$ipent['ip']." to any out") ; + } + $ruleno++ ; + if ($ruleno > 19899) + $ruleno = 10000; + } + + fclose($fd); + + /* write next rule number */ + $fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w"); + if ($fd) { + fwrite($fd, $ruleno); + fclose($fd); + } + } + + captiveportal_unlock() ; + return 0; +} + +/* get last activity timestamp given ipfw rule number */ +function captiveportal_get_last_activity($ruleno) { + + exec("/sbin/ipfw -T list {$ruleno} 2>/dev/null", $ipfwoutput); + + /* in */ + if ($ipfwoutput[0]) { + $ri = explode(" ", $ipfwoutput[0]); + if ($ri[1]) + return $ri[1]; + } + + return 0; +} + +/* read captive portal DB into array */ +function captiveportal_read_db() { + + global $g; + + $cpdb = array(); + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r"); + if ($fd) { + while (!feof($fd)) { + $line = trim(fgets($fd)); + if ($line) { + $cpdb[] = explode(",", $line); + } + } + fclose($fd); + } + return $cpdb; +} + +/* write captive portal DB */ +function captiveportal_write_db($cpdb) { + + global $g; + + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w"); + if ($fd) { + foreach ($cpdb as $cpent) { + fwrite($fd, join(",", $cpent) . "\n"); + } + fclose($fd); + } +} + +/* read RADIUS servers into array */ +function captiveportal_get_radius_servers() { + + global $g; + + if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) { + $fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r"); + if ($fd) { + $radiusservers = array(); + while (!feof($fd)) { + $line = trim(fgets($fd)); + if ($line) { + $radsrv = array(); + list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line); + $radiusservers[] = $radsrv; + } + } + fclose($fd); + + return $radiusservers; + } + } + + return false; +} + +/* lock captive portal information, decide that the lock file is stale after + 10 seconds */ +function captiveportal_lock() { + + global $g; + + $lockfile = "{$g['varrun_path']}/captiveportal.lock"; + + $n = 0; + while ($n < 10) { + /* open the lock file in append mode to avoid race condition */ + if ($fd = @fopen($lockfile, "x")) { + /* succeeded */ + fclose($fd); + return; + } else { + /* file locked, wait and try again */ + sleep(1); + $n++; + } + } +} + +/* unlock configuration file */ +function captiveportal_unlock() { + + global $g; + + $lockfile = "{$g['varrun_path']}/captiveportal.lock"; + + if (file_exists($lockfile)) + unlink($lockfile); +} + +?> diff --git a/etc/inc/config.inc b/etc/inc/config.inc new file mode 100644 index 0000000..58202aa --- /dev/null +++ b/etc/inc/config.inc @@ -0,0 +1,551 @@ +<?php +/* + config.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include globals/utility/XML parser files */ +require_once("globals.inc"); +require_once("util.inc"); +require_once("xmlparse.inc"); + +/* read platform */ +if (file_exists("{$g['etc_path']}/platform")) { + $g['platform'] = chop(file_get_contents("{$g['etc_path']}/platform")); +} else { + $g['platform'] = "unknown"; +} + +if ($g['booting']) { + /* find the device where config.xml resides and write out an fstab */ + unset($cfgdevice); + + /* check if there's already an fstab (NFS booting?) */ + if (!file_exists("{$g['etc_path']}/fstab")) { + + if (strstr($g['platform'], "cdrom")) { + /* config is on floppy disk for CD-ROM version */ + $cfgdevice = $cfgpartition = "fd0"; + $cfgfstype = "msdos"; + } else { + /* probe kernel known disks until we find one with config.xml */ + $disks = explode(" ", trim(preg_replace("/kern.disks: /", "", exec("/sbin/sysctl kern.disks")))); + foreach ($disks as $mountdisk) { + /* skip mfs mounted filesystems */ + if (strstr($mountdisk, "md")) + continue; + if (mwexec("/sbin/mount -r /dev/{$mountdisk}a {$g['cf_path']}") == 0) { + if (file_exists("{$g['cf_conf_path']}/config.xml")) { + /* found it */ + $cfgdevice = $mountdisk; + $cfgpartition = $cfgdevice . "a"; + $cfgfstype = "ufs"; + echo "Found configuration on $cfgdevice.\n"; + } + + mwexec("/sbin/umount -f {$g['cf_path']}"); + + if ($cfgdevice) + break; + } + } + } + + if (!$cfgdevice) { + /* no device found, print an error and die */ + echo <<<EOD + + +******************************************************************************* +* FATAL ERROR * +* The device that contains the configuration file (config.xml) could not be * +* found. m0n0wall cannot continue booting. * +******************************************************************************* + + +EOD; + + mwexec("/sbin/halt"); + exit; + } + + /* write device name to a file for rc.firmware */ + $fd = fopen("{$g['varetc_path']}/cfdevice", "w"); + fwrite($fd, $cfgdevice . "\n"); + fclose($fd); + + /* write out an fstab */ + $fd = fopen("{$g['etc_path']}/fstab", "w"); + + $fstab = "/dev/{$cfgpartition} {$g['cf_path']} {$cfgfstype} ro 1 1\n"; + $fstab .= "proc /proc procfs rw 0 0\n"; + + fwrite($fd, $fstab); + fclose($fd); + } + + /* mount all filesystems */ + mwexec("/sbin/mount -a"); +} + +/* parse configuration */ +if (!$noparseconfig) { + + config_lock(); + + /* see if there's a newer cache file */ + if (file_exists("{$g['tmp_path']}/config.cache") && + (filemtime("{$g['tmp_path']}/config.cache") >= + filemtime("{$g['conf_path']}/config.xml"))) { + + /* read cache */ + $config = unserialize(file_get_contents("{$g['tmp_path']}/config.cache")); + } else { + + if (!file_exists("{$g['conf_path']}/config.xml")) { + if ($g['booting']) { + if (strstr($g['platform'], "cdrom")) { + /* try copying the default config. to the floppy */ + reset_factory_defaults(); + + echo "No XML configuration file found - using factory defaults.\n"; + echo "Make sure that the configuration floppy disk with the conf/config.xml\n"; + echo "file is inserted. If it isn't, your configuration changes will be lost\n"; + echo "on reboot.\n"; + } else { + echo "XML configuration file not found. m0n0wall cannot continue booting.\n"; + mwexec("/sbin/halt"); + exit; + } + } else { + config_unlock(); + exit(0); + } + } + + $config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']); + + if ((float)$config['version'] > (float)$g['latest_config']) { + if ($g['booting']) { + echo <<<EOD + + +******************************************************************************* +* WARNING! * +* The current configuration has been created with a newer version of m0n0wall * +* than this one! This can lead to serious misbehavior and even security * +* holes! You are urged to either upgrade to a newer version of m0n0wall or * +* revert to the default configuration immediately! * +******************************************************************************* + + +EOD; + } + } + + /* write config cache */ + $fd = @fopen("{$g['tmp_path']}/config.cache", "wb"); + if ($fd) { + fwrite($fd, serialize($config)); + fclose($fd); + } + } + + config_unlock(); + + /* make alias table (for faster lookups) */ + alias_make_table(); +} + +/* mount flash card read/write */ +function conf_mount_rw() { + global $g; + + /* don't use mount -u anymore + (doesn't sync the files properly and /bin/sync won't help either) */ + mwexec("/sbin/umount -f {$g['cf_path']}"); + mwexec("/sbin/mount -w -o noatime {$g['cf_path']}"); +} + +/* mount flash card read only */ +function conf_mount_ro() { + global $g; + + mwexec("/sbin/umount -f {$g['cf_path']}"); + mwexec("/sbin/mount -r {$g['cf_path']}"); +} + +/* convert configuration, if necessary */ +function convert_config() { + global $config, $g; + + if ($config['version'] == $g['latest_config']) + return; /* already at latest version */ + + if ($g['booting']) + echo "Converting configuration... "; + + /* convert 1.0 -> 1.1 */ + if ($config['version'] == "1.0") { + $opti = 1; + $ifmap = array('lan' => 'lan', 'wan' => 'wan', 'pptp' => 'pptp'); + + /* convert DMZ to optional, if necessary */ + if (isset($config['interfaces']['dmz'])) { + + $dmzcfg = &$config['interfaces']['dmz']; + + if ($dmzcfg['if']) { + $config['interfaces']['opt' . $opti] = array(); + $optcfg = &$config['interfaces']['opt' . $opti]; + + $optcfg['enable'] = $dmzcfg['enable']; + $optcfg['descr'] = "DMZ"; + $optcfg['if'] = $dmzcfg['if']; + $optcfg['ipaddr'] = $dmzcfg['ipaddr']; + $optcfg['subnet'] = $dmzcfg['subnet']; + + $ifmap['dmz'] = "opt" . $opti; + $opti++; + } + + unset($config['interfaces']['dmz']); + } + + /* convert WLAN1/2 to optional, if necessary */ + for ($i = 1; isset($config['interfaces']['wlan' . $i]); $i++) { + + if (!$config['interfaces']['wlan' . $i]['if']) { + unset($config['interfaces']['wlan' . $i]); + continue; + } + + $wlancfg = &$config['interfaces']['wlan' . $i]; + $config['interfaces']['opt' . $opti] = array(); + $optcfg = &$config['interfaces']['opt' . $opti]; + + $optcfg['enable'] = $wlancfg['enable']; + $optcfg['descr'] = "WLAN" . $i; + $optcfg['if'] = $wlancfg['if']; + $optcfg['ipaddr'] = $wlancfg['ipaddr']; + $optcfg['subnet'] = $wlancfg['subnet']; + $optcfg['bridge'] = $wlancfg['bridge']; + + $optcfg['wireless'] = array(); + $optcfg['wireless']['mode'] = $wlancfg['mode']; + $optcfg['wireless']['ssid'] = $wlancfg['ssid']; + $optcfg['wireless']['channel'] = $wlancfg['channel']; + $optcfg['wireless']['wep'] = $wlancfg['wep']; + + $ifmap['wlan' . $i] = "opt" . $opti; + + unset($config['interfaces']['wlan' . $i]); + $opti++; + } + + /* convert filter rules */ + $n = count($config['filter']['rule']); + for ($i = 0; $i < $n; $i++) { + + $fr = &$config['filter']['rule'][$i]; + + /* remap interface */ + if (array_key_exists($fr['interface'], $ifmap)) + $fr['interface'] = $ifmap[$fr['interface']]; + else { + /* remove the rule */ + echo "\nWarning: filter rule removed " . + "(interface '{$fr['interface']}' does not exist anymore)."; + unset($config['filter']['rule'][$i]); + continue; + } + + /* remap source network */ + if (isset($fr['source']['network'])) { + if (array_key_exists($fr['source']['network'], $ifmap)) + $fr['source']['network'] = $ifmap[$fr['source']['network']]; + else { + /* remove the rule */ + echo "\nWarning: filter rule removed " . + "(source network '{$fr['source']['network']}' does not exist anymore)."; + unset($config['filter']['rule'][$i]); + continue; + } + } + + /* remap destination network */ + if (isset($fr['destination']['network'])) { + if (array_key_exists($fr['destination']['network'], $ifmap)) + $fr['destination']['network'] = $ifmap[$fr['destination']['network']]; + else { + /* remove the rule */ + echo "\nWarning: filter rule removed " . + "(destination network '{$fr['destination']['network']}' does not exist anymore)."; + unset($config['filter']['rule'][$i]); + continue; + } + } + } + + /* convert shaper rules */ + $n = count($config['pfqueueing']['rule']); + if (is_array($config['pfqueueing']['rule'])) + for ($i = 0; $i < $n; $i++) { + + $fr = &$config['pfqueueing']['rule'][$i]; + + /* remap interface */ + if (array_key_exists($fr['interface'], $ifmap)) + $fr['interface'] = $ifmap[$fr['interface']]; + else { + /* remove the rule */ + echo "\nWarning: traffic shaper rule removed " . + "(interface '{$fr['interface']}' does not exist anymore)."; + unset($config['pfqueueing']['rule'][$i]); + continue; + } + + /* remap source network */ + if (isset($fr['source']['network'])) { + if (array_key_exists($fr['source']['network'], $ifmap)) + $fr['source']['network'] = $ifmap[$fr['source']['network']]; + else { + /* remove the rule */ + echo "\nWarning: traffic shaper rule removed " . + "(source network '{$fr['source']['network']}' does not exist anymore)."; + unset($config['pfqueueing']['rule'][$i]); + continue; + } + } + + /* remap destination network */ + if (isset($fr['destination']['network'])) { + if (array_key_exists($fr['destination']['network'], $ifmap)) + $fr['destination']['network'] = $ifmap[$fr['destination']['network']]; + else { + /* remove the rule */ + echo "\nWarning: traffic shaper rule removed " . + "(destination network '{$fr['destination']['network']}' does not exist anymore)."; + unset($config['pfqueueing']['rule'][$i]); + continue; + } + } + } + + $config['version'] = "1.1"; + } + + /* convert 1.1 -> 1.2 */ + if ($config['version'] == "1.1") { + /* move LAN DHCP server config */ + $tmp = $config['dhcpd']; + $config['dhcpd'] = array(); + $config['dhcpd']['lan'] = $tmp; + + /* encrypt password */ + $config['system']['password'] = crypt($config['system']['password']); + + $config['version'] = "1.2"; + } + + /* convert 1.2 -> 1.3 */ + if ($config['version'] == "1.2") { + /* convert advanced outbound NAT config */ + for ($i = 0; isset($config['nat']['advancedoutbound']['rule'][$i]); $i++) { + $curent = &$config['nat']['advancedoutbound']['rule'][$i]; + $src = $curent['source']; + $curent['source'] = array(); + $curent['source']['network'] = $src; + $curent['destination'] = array(); + $curent['destination']['any'] = true; + } + + /* add an explicit type="pass" to all filter rules to make things consistent */ + for ($i = 0; isset($config['filter']['rule'][$i]); $i++) { + $config['filter']['rule'][$i]['type'] = "pass"; + } + + $config['version'] = "1.3"; + } + + /* convert 1.3 -> 1.4 */ + if ($config['version'] == "1.3") { + /* convert shaper rules (make pipes) */ + if (is_array($config['pfqueueing']['rule'])) { + $config['pfqueueing']['pipe'] = array(); + + for ($i = 0; isset($config['pfqueueing']['rule'][$i]); $i++) { + $curent = &$config['pfqueueing']['rule'][$i]; + + /* make new pipe and associate with this rule */ + $newpipe = array(); + $newpipe['descr'] = $curent['descr']; + $newpipe['bandwidth'] = $curent['bandwidth']; + $newpipe['delay'] = $curent['delay']; + $newpipe['mask'] = $curent['mask']; + $config['pfqueueing']['pipe'][$i] = $newpipe; + + $curent['targetpipe'] = $i; + + unset($curent['bandwidth']); + unset($curent['delay']); + unset($curent['mask']); + } + } + + $config['version'] = "1.4"; + } + + write_config(); + + if ($g['booting']) + echo "done\n"; +} + +/* save the system configuration */ +function write_config() { + + global $config, $g; + + config_lock(); + + conf_mount_rw(); + + if (time() > mktime(0, 0, 0, 9, 1, 2004)) /* make sure the clock settings is plausible */ + $config['lastchange'] = time(); + + /* generate configuration XML */ + $xmlconfig = dump_xml_config($config, $g['xml_rootobj']); + + /* write configuration */ + $fd = fopen("{$g['cf_conf_path']}/config.xml", "w"); + + if (!$fd) + die("Unable to open config.xml for writing in write_config()\n"); + + fwrite($fd, $xmlconfig); + fclose($fd); + + conf_mount_ro(); + + /* re-read configuration */ + $config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']); + + /* write config cache */ + $fd = @fopen("{$g['tmp_path']}/config.cache", "wb"); + if ($fd) { + fwrite($fd, serialize($config)); + fclose($fd); + } + + config_unlock(); +} + +function reset_factory_defaults() { + + global $g; + + config_lock(); + + conf_mount_rw(); + + /* create conf directory, if necessary */ + if (!file_exists("{$g['cf_conf_path']}")) + @mkdir("{$g['cf_conf_path']}"); + + /* clear out /conf */ + $dh = opendir($g['conf_path']); + while ($filename = readdir($dh)) { + if (($filename != ".") && ($filename != "..")) { + unlink($g['conf_path'] . "/" . $filename); + } + } + closedir($dh); + + /* copy default configuration */ + @copy("{$g['conf_default_path']}/config.xml", "{$g['conf_path']}/config.xml"); + + conf_mount_ro(); + + config_unlock(); + + return 0; +} + +function config_install($conffile) { + + global $config, $g; + + if (!file_exists($conffile)) + return 1; + + config_lock(); + conf_mount_rw(); + + copy($conffile, "{$g['conf_path']}/config.xml"); + + conf_mount_ro(); + config_unlock(); + + return 0; +} + +/* lock configuration file, decide that the lock file is stale after + 10 seconds */ +function config_lock() { + + global $g; + + $lockfile = "{$g['varrun_path']}/config.lock"; + + $n = 0; + while ($n < 10) { + /* open the lock file in append mode to avoid race condition */ + if ($fd = @fopen($lockfile, "x")) { + /* succeeded */ + fclose($fd); + return; + } else { + /* file locked, wait and try again */ + sleep(1); + $n++; + } + } +} + +/* unlock configuration file */ +function config_unlock() { + + global $g; + + $lockfile = "{$g['varrun_path']}/config.lock"; + + if (file_exists($lockfile)) + unlink($lockfile); +} + +?> diff --git a/etc/inc/filter.inc b/etc/inc/filter.inc new file mode 100644 index 0000000..1b409f8 --- /dev/null +++ b/etc/inc/filter.inc @@ -0,0 +1,946 @@ +<?php +/* + filter.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); + +function filter_resync() { + global $config, $g; + + mwexec("/sbin/pfctl -y"); /* XXX */ +} + +function filter_ipmon_start() { + global $config, $g; + + mwexec("/pflogd -sD"); +} + +function filter_configure() { + global $config, $g; + + if ($g['booting']) + echo "Configuring firewall... "; + + /* set TCP timeouts */ + $tcpidletimeout = 9000; + if ($config['filter']['tcpidletimeout']) + $tcpidletimeout = $config['filter']['tcpidletimeout']; + mwexec("/sbin/sysctl net.inet.ipf.fr_tcpidletimeout={$tcpidletimeout}"); + mwexec("/sbin/sysctl net.inet.ipf.fr_tcphalfclosed=480"); + + /* generate pfctl rules */ + $natrules = filter_nat_rules_generate(); + /* generate pfctl rules */ + $pfrules = filter_rules_generate(); + /* generate altq interface setup parms */ + $altq_ints = filter_setup_altq_interfaces(); + /* generate altq queues */ + $altq_queues = filter_generate_altq_queues(); + + mwexec("/sbin/pfctl -e"); + mwexec("/sbin/pfctl -F nat"); + mwexec("/sbin/pfctl -F rules"); + + /* get our wan interface? */ + $wanif = get_real_wan_interface(); + + $fd = fopen("/tmp/rules.debug", "w"); + fwrite($fd, "set loginterface $wanif \n"); + fwrite($fd, "set optimization aggressive\n"); + fwrite($fd, $altq_ints); + fwrite($fd, $altq_queues); + fwrite($fd, $natrules); + fwrite($fd, $pfrules); + fclose($fd); + + mwexec("chmod a+x /tmp/rules.debug"); + mwexec("/sbin/pfctl -f /tmp/rules.debug"); + + /* set up MSS clamping */ + if ($config['interfaces']['wan']['mtu']) + $mssclamp = $config['interfaces']['wan']['mtu'] - 40; + else if ($config['interfaces']['wan']['ipaddr'] == "pppoe") + $mssclamp = 1452; + else + $mssclamp = 0; + + mwexec("/sbin/sysctl net.inet.ipf.fr_mssif={$wanif}"); + mwexec("/sbin/sysctl net.inet.ipf.fr_mssclamp={$mssclamp}"); + + if ($g['booting']) + echo "done\n"; + + return 0; +} + +function filter_generate_altq_queues() { + global $config; + $altq_rules = ""; + if (is_array($config['pfqueueing']['queue'])) { + foreach ($config['pfqueueing']['queue'] as $rule) { + $altq_rules .= "queue " . $rule['name'] . " "; + if (isset($rule['bandwidth'])) + $altq_rules .= "bandwidth " . $rule['bandwidth'] . " "; + if (isset($rule['priority'])) + $altq_rules .= "priority " . $rule['priority'] . " "; + if (isset($rule['options'])) /* XXX turn options into an xml array */ + $altq_rules .= $rule['schedulertype'] . "(". $rule['options'] . ")"; + if (isset($rule['subqueue'])) { + $altq_rules .= "{ "; + $fsq = ""; + foreach ($rule['subqueue'] as $sq) { + if($fsq) $altq_rules .= ","; + $altq_rules .= $sq['name']; + $fsq = "1"; + } + $altq_rules .= " }"; + } + $altq_rules .= "\n"; + } + } + return $altq_rules; +} + +function filter_setup_altq_interfaces() { + global $config; + $altq_rules = ""; + $queue_names = ""; + $is_first = ""; + if (is_array($config['pfqueueing']['queue'])) { + foreach ($config['pfqueueing']['queue'] as $queue) { + if(is_subqueue($queue['name']) == 0) { + if($is_first) $queue_names .= ", "; + $queue_names .= $queue['name']; + $is_first = "1"; + } + } + } + if (is_array($config['interfaces'])) { + foreach ($config['interfaces'] as $ifname) { + if(isset($ifname['bandwidth'])) { + $subnet = $ifname['ipaddr'] . "/" . $ifname['subnet']; + $altq_rules .= "altq on " . $ifname['if'] . " "; + $altq_rules .= $ifname['schedulertype'] . " bandwidth " . $ifname['bandwidth'] . " "; + if($queue_names <> "") + $altq_rules .= "queue { " . $queue_names . " }"; + $altq_rules .= "\n"; + } + } + } + return $altq_rules; +} + +function is_subqueue($name) { + global $config; + $status = ""; + if (is_array($config['pfqueueing']['queue'])) { + foreach ($config['pfqueueing']['queue'] as $queue) { + if(is_array($queue['subqueue'])) { + foreach ($queue['subqueue'] as $sq) { + if($sq['name'] == $name) return 1; + } + } + } + } + return 0; +} + +function filter_flush_nat_table() { + global $config, $g; + + return mwexec("/sbin/pfctl -F nat"); +} + +function filter_flush_state_table() { + global $config, $g; + + return mwexec("/sbin/pfctl -F state"); +} + +function filter_nat_rules_generate_if($if, $src, $dst, $target) { + + if ($target) + $tgt = $target . "/32"; + else + $tgt = "0/32"; + + $natrule = <<<EOD +nat on $if from $src to any -> $if + +EOD; + + return $natrule; +} + +function filter_nat_rules_generate() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + $lancfg = $config['interfaces']['lan']; + + $pptpdcfg = $config['pptpd']; + $wanif = get_real_wan_interface(); + + $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']); + + $natrules = ""; + + /* any 1:1 mappings? */ + if (is_array($config['nat']['onetoone'])) { + foreach ($config['nat']['onetoone'] as $natent) { + if (!is_numeric($natent['subnet'])) + $sn = 32; + else + $sn = $natent['subnet']; + + if (!$natent['interface'] || ($natent['interface'] == "wan")) + $natif = $wanif; + else + $natif = $config['interfaces'][$natent['interface']]['if']; + + $natrules .= "binat on {$natif} from {$natent['internal']}/{$sn} to any -> {$natent['external']}\n"; + } + } + + /* outbound rules - advanced or standard */ + if (isset($config['nat']['advancedoutbound']['enable'])) { + /* advanced outbound rules */ + if (is_array($config['nat']['advancedoutbound']['rule'])) { + foreach ($config['nat']['advancedoutbound']['rule'] as $obent) { + $dst = ""; + $src = ""; + if (!isset($obent['destination']['any'])) { + $src = "from "; + if (isset($obent['destination']['not'])) + $dst = "! to "; + else + $dst = "to "; + $dst .= $obent['destination']['network']; + } + $src .= $obent['source']['network']; + + if (!$obent['interface'] || ($obent['interface'] == "wan")) + $natif = $wanif; + else + $natif = $config['interfaces'][$obent['interface']]['if']; + + $natrules .= filter_nat_rules_generate_if($natif, $src, $dst, + $obent['target']); + } + } + } else { + /* standard outbound rules (one for each interface) */ + $natrules .= filter_nat_rules_generate_if($wanif, + $lansa . "/" . $lancfg['subnet'], "", null); + + /* optional interfaces */ + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) { + $optcfg = $config['interfaces']['opt' . $i]; + + if (isset($optcfg['enable']) && !$optcfg['bridge']) { + $optsa = gen_subnet($optcfg['ipaddr'], $optcfg['subnet']); + $natrules .= filter_nat_rules_generate_if($wanif, + $optsa . "/" . $optcfg['subnet'], "", null); + } + } + + /* PPTP subnet */ + if ($pptpdcfg['mode'] == "server") { + $natrules .= filter_nat_rules_generate_if($wanif, + $pptpdcfg['remoteip'] . "/" . $g['pptp_subnet'], "", null); + } + + /* static routes */ + if (is_array($config['staticroutes']['route'])) { + foreach ($config['staticroutes']['route'] as $route) { + if ($route['interface'] != "wan") + $natrules .= filter_nat_rules_generate_if($wanif, + $route['network'], "", null); + } + } + } + + /* DIAG: add ipv6 NAT, if requested */ + if (isset($config['diag']['ipv6nat']['enable'])) { + $natrules .= "rdr on $wanif proto ipv6 from any to any port 0 -> " . + "{$config['diag']['ipv6nat']['ipaddr']}\n"; + } + + if (isset($config['nat']['rule'])) { + foreach ($config['nat']['rule'] as $rule) { + + $extport = explode("-", $rule['external-port']); + $target = alias_expand_host($rule['target']); + + if (!$target) + continue; /* unresolvable alias */ + + if ($rule['external-address']) + $extaddr = $rule['external-address'] . "/32"; + else + $extaddr = "0/0"; + + if (!$rule['interface'] || ($rule['interface'] == "wan")) + $natif = $wanif; + else + $natif = $config['interfaces'][$rule['interface']]['if']; + + $lanif = $lancfg['if']; + + if ((!$extport[1]) || ($extport[0] == $extport[1])) { + $natrules .= + "rdr on $natif proto " . $rule['protocol'] . " from any to any port {$extport[0]} -> {$target} \n"; + } else { + $natrules .= + "rdr on $natif proto " . $rule['protocol']. " from any to any port {$extport[0]}:{$extport[1]} " . + "-> {$target} \n"; + } + + $natrules .= "\n"; + } + } + + if ($pptpdcfg['mode'] && $pptpdcfg['mode'] != "off") { + + if ($pptpdcfg['mode'] == "server") + $pptpdtarget = "127.0.0.1"; + else if ($pptpdcfg['mode'] == "redir") + $pptpdtarget = $pptpdcfg['redir']; + + if ($pptpdtarget) { + + $natrules .= <<<EOD + +# PPTP +rdr on $wanif proto gre from any to any port 0 -> $pptpdtarget +rdr on $wanif proto tcp from any to any port 1723 -> $pptpdtarget + +EOD; + } + } + + return $natrules; +} + +function filter_rules_generate() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + $lancfg = $config['interfaces']['lan']; + $pptpdcfg = $config['pptpd']; + + $lanif = $lancfg['if']; + $wanif = get_real_wan_interface(); + + /* rule groups (optional interfaces: see below) */ + $ifgroups = array("lan" => 100, "wan" => 200); + + $lanip = $lancfg['ipaddr']; + $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']); + $lansn = $lancfg['subnet']; + + /* optional interfaces */ + $optcfg = array(); + + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) { + $oc = $config['interfaces']['opt' . $i]; + + if (isset($oc['enable']) && $oc['if']) { + $oic = array(); + $oic['if'] = $oc['if']; + + if ($oc['bridge']) { + if (!strstr($oc['bridge'], "opt") || + isset($config['interfaces'][$oc['bridge']]['enable'])) { + if (is_ipaddr($config['interfaces'][$oc['bridge']]['ipaddr'])) { + $oic['ip'] = $config['interfaces'][$oc['bridge']]['ipaddr']; + $oic['sn'] = $config['interfaces'][$oc['bridge']]['subnet']; + $oic['sa'] = gen_subnet($oic['ip'], $oic['sn']); + } + } + $oic['bridge'] = 1; + } else { + $oic['ip'] = $oc['ipaddr']; + $oic['sn'] = $oc['subnet']; + $oic['sa'] = gen_subnet($oic['ip'], $oic['sn']); + } + + $optcfg['opt' . $i] = $oic; + $ifgroups['opt' . $i] = ($i * 100) + 200; + } + } + + if ($pptpdcfg['mode'] == "server") { + $pptpip = $pptpdcfg['localip']; + $pptpsa = $pptpdcfg['remoteip']; + $pptpsn = $g['pptp_subnet']; + } + + /* default block logging? */ + if (!isset($config['syslog']['nologdefaultblock'])) + $log = "log"; + else + $log = ""; + + $ipfrules = <<<EOD + +# loopback +pass in quick on lo0 all +pass out quick on lo0 all + +# allow access to DHCP server on LAN +pass in quick on $lanif proto udp from any port = 68 to 255.255.255.255 port = 67 +pass in quick on $lanif proto udp from any port = 68 to $lanip port = 67 +pass out quick on $lanif proto udp from $lanip port = 67 to any port = 68 + +EOD; + + /* allow access to DHCP server on optional interfaces */ + foreach ($optcfg as $on => $oc) { + if (isset($config['dhcpd'][$on]['enable']) && (!$oc['bridge'])) { + $ipfrules .= <<<EOD + +# allow access to DHCP server on {$on} +pass in quick on {$oc['if']} proto udp from any port = 68 to 255.255.255.255 port = 67 +pass in quick on {$oc['if']} proto udp from any port = 68 to {$oc['ip']} port = 67 +pass out quick on {$oc['if']} proto udp from {$oc['ip']} port = 67 to any port = 68 + +EOD; + } + } + + /* pass traffic between statically routed subnets and the subnet on the + interface in question to avoid problems with complicated routing + topologies */ + if (is_array($config['staticroutes']['route']) && count($config['staticroutes']['route'])) { + foreach ($config['staticroutes']['route'] as $route) { + unset($sa); + + if ($route['interface'] == "lan") { + $sa = $lansa; + $sn = $lansn; + $if = $lanif; + } else if (strstr($route['interface'], "opt")) { + $oc = $optcfg[$route['interface']]; + if ($oc['ip']) { + $sa = $oc['sa']; + $sn = $oc['sn']; + $if = $oc['if']; + } + } + + if ($sa) { + $ipfrules .= <<<EOD +pass in quick on {$if} from {$sa}/{$sn} to {$route['network']} +pass in quick on {$if} from {$route['network']} to {$sa}/{$sn} +pass out quick on {$if} from {$sa}/{$sn} to {$route['network']} +pass out quick on {$if} from {$route['network']} to {$sa}/{$sn} + +EOD; + } + } + } + + $ipfrules .= <<<EOD + +# WAN spoof check +block in $log quick on $wanif from $lansa/$lansn to any + +EOD; + + foreach ($optcfg as $oc) { + if (!$oc['bridge']) + $ipfrules .= "block in $log quick on $wanif from {$oc['sa']}/{$oc['sn']} to any\n"; + } + + /* allow PPTP traffic if PPTP client is enabled on WAN */ + if ($wancfg['ipaddr'] == "pptp") { + $ipfrules .= <<<EOD + +# allow PPTP client +pass in quick on {$wancfg['if']} proto gre from any to any +pass out quick on {$wancfg['if']} proto gre from any to any +pass in quick on {$wancfg['if']} proto tcp from any port = 1723 to any +pass out quick on {$wancfg['if']} proto tcp from any to any port = 1723 + +EOD; + } + + $ipfrules .= <<<EOD + +# allow our DHCP client out to the WAN +# XXX - should be more restrictive +# (not possible at the moment - need 'me' like in ipfw) +pass out quick on $wanif proto udp from any port = 68 to any port = 67 +block in $log quick on $wanif proto udp from any port = 67 to $lansa/$lansn port = 68 +pass in quick on $wanif proto udp from any port = 67 to any port = 68 + +# LAN/OPT spoof check (needs to be after DHCP because of broadcast addresses) + +EOD; + + /* LAN spoof check */ + $ipfrules .= filter_rules_spoofcheck_generate('lan', $lanif, $lansa, $lansn, $log); + + /* OPT spoof check */ + foreach ($optcfg as $on => $oc) { + if ($oc['ip']) + $ipfrules .= filter_rules_spoofcheck_generate($on, $oc['if'], $oc['sa'], $oc['sn'], $log); + } + + /* block private networks on WAN? */ + if (isset($config['interfaces']['wan']['blockpriv'])) { + $ipfrules .= <<<EOD + +# block anything from private networks on WAN interface +block in $log quick on $wanif from 10.0.0.0/8 to any +block in $log quick on $wanif from 127.0.0.0/8 to any +block in $log quick on $wanif from 172.16.0.0/12 to any +block in $log quick on $wanif from 192.168.0.0/16 to any + +EOD; + } + + /* IPsec enabled? */ + if (isset($config['ipsec']['enable']) && + ((is_array($config['ipsec']['tunnel']) && + count($config['ipsec']['tunnel'])) || + isset($config['ipsec']['mobileclients']['enable']))) { + + $curwanip = get_current_wan_address(); + + if ($curwanip) + $ipfrules .= filter_rules_ipsec_generate($wanif, $curwanip); + + $ipfrules .= filter_rules_ipsec_generate($lanif, $lanip); + + foreach ($optcfg as $on => $oc) { + if ($oc['ip']) + $ipfrules .= filter_rules_ipsec_generate($oc['if'], $oc['ip']); + } + } + + /* XXX - the first section is only needed because pfctl refuses to + parse rules that have "flags S/SAFR" and proto "tcp/udp" set because + UDP does not have flags, but we still want to offer the TCP/UDP protocol + option to the user */ + + $ipfrules .= <<<EOD + + +# let out anything from the firewall host itself and decrypted IPsec traffic +pass out quick on $wanif all keep state + +EOD; + + /* group heads for optional interfaces */ + foreach ($optcfg as $on => $oc) { + + $ingroup = $ifgroups[$on]; + + $ipfrules .= <<<EOD + + +# let out anything from the firewall host itself and decrypted IPsec traffic +pass out quick on {$oc['if']} all keep state + +EOD; + + } + + if (!isset($config['system']['webgui']['noantilockout'])) { + + $ipfrules .= <<<EOD + +# make sure the user cannot lock himself out of the webGUI +pass in quick from $lansa/$lansn to $lanip keep state +# group 100 + +EOD; + } + + /* PPTPd enabled? */ + if ($pptpdcfg['mode'] && ($pptpdcfg['mode'] != "off")) { + + if ($pptpdcfg['mode'] == "server") + $pptpdtarget = "127.0.0.1"; + else + $pptpdtarget = $pptpdcfg['redir']; + + $ipfrules .= <<<EOD + +# PPTP rules +pass in quick proto gre from any to $pptpdtarget keep state +# group 200 +pass in quick proto tcp from any to $pptpdtarget port = 1723 keep state +# group 200 + +EOD; + } + + /* BigPond client enabled? */ + if ($wancfg['ipaddr'] == "bigpond") { + + $ipfrules .= <<<EOD + +# BigPond heartbeat rules +pass in quick proto udp from any to any port = 5050 keep state +# group 200 + +EOD; + } + + $i = 0; + + $ipfrules .= "\n# User-defined rules follow\n"; + + if (isset($config['filter']['rule'])) + foreach ($config['filter']['rule'] as $rule) { + + /* don't include disabled rules */ + if (isset($rule['disabled'])) { + $i++; + continue; + } + + /* does the rule deal with a PPTP interface? */ + if ($rule['interface'] == "pptp") { + + if ($pptpdcfg['mode'] != "server") { + $i++; + continue; + } + + $nif = $g['n_pptp_units']; + $ispptp = true; + } else { + + if (strstr($rule['interface'], "opt")) { + if (!array_key_exists($rule['interface'], $optcfg)) { + $i++; + continue; + } + } + + $nif = 1; + $ispptp = false; + } + + if ($pptpdcfg['mode'] != "server") { + if (($rule['source']['network'] == "pptp") || + ($rule['destination']['network'] == "pptp")) { + $i++; + continue; + } + } + + if ($rule['source']['network'] && strstr($rule['source']['network'], "opt")) { + if (!array_key_exists($rule['source']['network'], $optcfg)) { + $i++; + continue; + } + } + if ($rule['destination']['network'] && strstr($rule['destination']['network'], "opt")) { + if (!array_key_exists($rule['destination']['network'], $optcfg)) { + $i++; + continue; + } + } + + /* check for unresolvable aliases */ + if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) { + $i++; + continue; + } + if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) { + $i++; + continue; + } + + for ($iif = 0; $iif < $nif; $iif++) { + + if (!$ispptp) { + + $groupnum = $ifgroups[$rule['interface']]; + + if (!$groupnum) { + printf("Invalid interface name in rule $i\n"); + break; + } + } + + $type = $rule['type']; + if ($type != "pass" && $type != "block" && $type != "reject") { + /* default (for older rules) is pass */ + $type = "pass"; + } + + if ($type == "reject") { + /* special reject packet */ + if ($rule['protocol'] == "tcp") { + $line = "block return-rst"; + } else if ($rule['protocol'] == "udp") { + $line = "block return-icmp"; + } else { + $line = "block"; + } + } else { + $line = $type; + } + + if(!isset($rule['direction'])) { + $line .= " in "; + } else { + $line .= " " . $rule['direction'] . " "; + } + + if (isset($rule['log'])) + $line .= "log "; + + $line .= "quick "; + + if ($ispptp) { + $line .= "on ng" . ($iif+1) . " "; + } + + if (isset($rule['protocol'])) { + $line .= "proto {$rule['protocol']} "; + } + + /* source address */ + if (isset($rule['source']['any'])) { + $src = "any"; + } else if ($rule['source']['network']) { + + if (strstr($rule['source']['network'], "opt")) { + $src = $optcfg[$rule['source']['network']]['sa'] . "/" . + $optcfg[$rule['source']['network']]['sn']; + } else { + switch ($rule['source']['network']) { + case 'lan': + $src = "$lansa/$lansn"; + break; + case 'pptp': + $src = "$pptpsa/$pptpsn"; + break; + } + } + } else if ($rule['source']['address']) { + $src = alias_expand($rule['source']['address']); + } + + if (!$src || ($src == "/")) { + //printf("No source address found in rule $i\n"); + break; + } + + if (isset($rule['source']['not'])) { + $line .= "from !$src "; + } else { + $line .= "from $src "; + } + + if (in_array($rule['protocol'], array("tcp","udp","tcp/udp"))) { + + if ($rule['source']['port']) { + $srcport = explode("-", $rule['source']['port']); + + if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) { + $line .= "port = {$srcport[0]} "; + } else if (($srcport[0] == 1) && ($srcport[1] == 65535)) { + /* no need for a port statement here */ + } else if ($srcport[1] == 65535) { + $line .= "port >= {$srcport[0]} "; + } else if ($srcport[0] == 1) { + $line .= "port <= {$srcport[1]} "; + } else { + $srcport[0]--; + $srcport[1]++; + $line .= "port {$srcport[0]} >< {$srcport[1]} "; + } + } + } + + /* destination address */ + if (isset($rule['destination']['any'])) { + $dst = "any"; + } else if ($rule['destination']['network']) { + + if (strstr($rule['destination']['network'], "opt")) { + $dst = $optcfg[$rule['destination']['network']]['sa'] . "/" . + $optcfg[$rule['destination']['network']]['sn']; + } else { + switch ($rule['destination']['network']) { + case 'lan': + $dst = "$lansa/$lansn"; + break; + case 'pptp': + $dst = "$pptpsa/$pptpsn"; + break; + } + } + } else if ($rule['destination']['address']) { + $dst = alias_expand($rule['destination']['address']); + } + + if (!$dst || ($dst == "/")) { + //printf("No destination address found in rule $i\n"); + break; + } + + if (isset($rule['destination']['not'])) { + $line .= "to !$dst "; + } else { + $line .= "to $dst "; + } + + if (in_array($rule['protocol'], array("tcp","udp","tcp/udp"))) { + + if ($rule['destination']['port']) { + $dstport = explode("-", $rule['destination']['port']); + + if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) { + $line .= "port = {$dstport[0]} "; + } else if (($dstport[0] == 1) && ($dstport[1] == 65535)) { + /* no need for a port statement here */ + } else if ($dstport[1] == 65535) { + $line .= "port >= {$dstport[0]} "; + } else if ($dstport[0] == 1) { + $line .= "port <= {$dstport[1]} "; + } else { + $dstport[0]--; + $dstport[1]++; + $line .= "port {$dstport[0]} >< {$dstport[1]} "; + } + } + } + + if (($rule['protocol'] == "icmp") && $rule['icmptype']) { + $line .= "icmp-type {$rule['icmptype']} "; + } + + if ($type == "pass") { + $line .= "keep state "; + + if (isset($rule['frags'])) + $line .= "keep frags "; + } + + if ($type == "reject" && $rule['protocol'] == "tcp") { + /* special reject packet */ + $line .= "flags S/SA "; + } + + if (isset($rule['flags'])) { + $line .= "flags " . $rule['flags'] . " "; + } + + if (!$ispptp) { + #$line .= "group $groupnum "; + } + + if (isset($rule['queuename'])) { + $line .= "queue " . $rule['queuename']; + } + + $line .= "\n"; + + $ipfrules .= $line; + } + + $i++; + } + + $ipfrules .= <<<EOD + +#--------------------------------------------------------------------------- +# default rules (just to be sure) +#--------------------------------------------------------------------------- +block in $log quick all +block out $log quick all + +EOD; + + return $ipfrules; +} + +function filter_rules_spoofcheck_generate($ifname, $if, $sa, $sn, $log) { + + global $g, $config; + + $ipfrules = ""; + + if (is_array($config['staticroutes']['route']) && count($config['staticroutes']['route'])) { + /* count rules */ + $n = 1; + foreach ($config['staticroutes']['route'] as $route) { + if ($route['interface'] == $ifname) + $n++; + } + + /* output skip rules */ + foreach ($config['staticroutes']['route'] as $route) { + if ($route['interface'] == $ifname) { + $ipfrules .= "skip $n in on $if from {$route['network']} to any\n"; + $n--; + } + } + $ipfrules .= "skip 1 in on $if from $sa/$sn to any\n"; + $ipfrules .= "#block in $log quick on $if all\n"; + } else { + $ipfrules .= "#block in $log quick on $if from ! $sa/$sn to any\n"; + } + + return $ipfrules; +} + +function filter_rules_ipsec_generate($ifname, $ip) { + + $ipfrules = <<<EOD + +# Pass IKE packets +pass in quick on {$ifname} proto udp from any to {$ip} port = 500 +pass out quick on {$ifname} proto udp from {$ip} port = 500 to any + +# Pass ESP packets +pass in quick on {$ifname} proto esp from any to {$ip} +pass out quick on {$ifname} proto esp from {$ip} to any + +# Pass AH packets +pass in quick on {$ifname} proto ah from any to {$ip} +pass out quick on {$ifname} proto ah from {$ip} to any + +EOD; + + return $ipfrules; +} + +?> diff --git a/etc/inc/functions.inc b/etc/inc/functions.inc new file mode 100644 index 0000000..eab4b82 --- /dev/null +++ b/etc/inc/functions.inc @@ -0,0 +1,41 @@ +<?php +/* + functions.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("system.inc"); +require_once("interfaces.inc"); +require_once("services.inc"); +require_once("filter.inc"); +require_once("shaper.inc"); +require_once("vpn.inc"); +require_once("captiveportal.inc"); +require_once("openvpn.inc"); + +?> diff --git a/etc/inc/globals.inc b/etc/inc/globals.inc new file mode 100644 index 0000000..eef6cff --- /dev/null +++ b/etc/inc/globals.inc @@ -0,0 +1,54 @@ +<?php +/* + globals.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +$g = array( + "varrun_path" => "/var/run", + "varetc_path" => "/var/etc", + "vardb_path" => "/var/db", + "varlog_path" => "/var/log", + "etc_path" => "/etc", + "tmp_path" => "/tmp", + "conf_path" => "/conf", + "ftmp_path" => "/ftmp", + "conf_default_path" => "/conf.default", + "cf_path" => "/cf", + "cf_conf_path" => "/cf/conf", + "www_path" => "/usr/local/www", + "captiveportal_path" => "/usr/local/captiveportal", + "xml_rootobj" => "m0n0wall", + "pppoe_interface" => "ng0", + "n_pptp_units" => 16, + "pptp_subnet" => 28, + "debug" => false, + "latest_config" => "1.4", + "nopccard_platforms" => array("wrap", "net48xx") +); + +?> diff --git a/etc/inc/interfaces.inc b/etc/inc/interfaces.inc new file mode 100644 index 0000000..00331c1 --- /dev/null +++ b/etc/inc/interfaces.inc @@ -0,0 +1,740 @@ +<?php +/* + interfaces.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); + +function interfaces_loopback_configure() { + global $config, $g; + + mwexec("/sbin/ifconfig lo0 127.0.0.1"); + + return 0; +} + +function interfaces_vlan_configure() { + global $config, $g; + + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + + /* load the VLAN module */ + mwexec("/sbin/kldload if_vlan"); + + /* devices with native VLAN support */ + $vlan_native_supp = explode(" ", "bge em gx nge ti txp"); + + /* devices with long frame support */ + $vlan_long_supp = explode(" ", "dc fxp sis ste tl tx xl"); + + $i = 0; + + foreach ($config['vlans']['vlan'] as $vlan) { + + $cmd = "/sbin/ifconfig vlan{$i} create vlan " . + escapeshellarg($vlan['tag']) . " vlandev " . + escapeshellarg($vlan['if']); + + /* get driver name */ + for ($j = 0; $j < strlen($vlan['if']); $j++) { + if ($vlan['if'][$j] >= '0' && $vlan['if'][$j] <= '9') + break; + } + $drvname = substr($vlan['if'], 0, $j); + + if (in_array($drvname, $vlan_native_supp)) + $cmd .= " link0"; + else if (in_array($drvname, $vlan_long_supp)) + $cmd .= " mtu 1500"; + + mwexec($cmd); + + /* make sure the parent interface is up */ + mwexec("/sbin/ifconfig " . escapeshellarg($vlan['if']) . " up"); + + $i++; + } + } + + return 0; +} + +function interfaces_lan_configure() { + global $config, $g; + + if ($g['booting']) + echo "Configuring LAN interface... "; + + $lancfg = $config['interfaces']['lan']; + + /* wireless configuration? */ + if (is_array($lancfg['wireless'])) + interfaces_wireless_configure($lancfg['if'], $lancfg['wireless']); + + /* MAC spoofing? */ + if ($lancfg['spoofmac']) + mwexec("/sbin/ifconfig " . escapeshellarg($lancfg['if']) . + " link " . escapeshellarg($lancfg['spoofmac'])); + + /* media */ + if ($lancfg['media'] || $lancfg['mediaopt']) { + $cmd = "/sbin/ifconfig " . escapeshellarg($lancfg['if']); + if ($lancfg['media']) + $cmd .= " media " . escapeshellarg($lancfg['media']); + if ($lancfg['mediaopt']) + $cmd .= " mediaopt " . escapeshellarg($lancfg['mediaopt']); + mwexec($cmd); + } + + mwexec("/sbin/ifconfig " . escapeshellarg($lancfg['if']) . " " . + escapeshellarg($lancfg['ipaddr'] . "/" . $lancfg['subnet'])); + + if (!$g['booting']) { + /* make new hosts file */ + system_hosts_generate(); + + /* reconfigure static routes (kernel may have deleted them) */ + system_routing_configure(); + + /* reload ipfilter (address may have changed) */ + filter_configure(); + + /* reload shaper (subnet may have changed) */ + shaper_configure(); + + /* reload IPsec tunnels */ + vpn_ipsec_configure(); + + /* reload dhcpd (gateway may have changed) */ + services_dhcpd_configure(); + + /* reload dnsmasq */ + services_dnsmasq_configure(); + + /* reload webgui */ + system_webgui_start(); + + /* reload captive portal */ + captiveportal_configure(); + } + + if ($g['booting']) + echo "done\n"; + + return 0; +} + +function interfaces_optional_configure() { + global $config, $g; + global $bridgeconfig; + + /* Reset bridge configuration. Interfaces will add to it. */ + $bridgeconfig = ""; + + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) { + interfaces_optional_configure_if($i); + } + + if ($bridgeconfig) { + /* Set the system bridge configuration and enable bridging. */ + mwexec("/sbin/sysctl net.link.ether.bridge_cfg=" . $bridgeconfig); + + if (isset($config['bridge']['filteringbridge'])) + mwexec("/sbin/sysctl net.link.ether.bridge_ipf=1"); + + mwexec("/sbin/sysctl net.link.ether.bridge=1"); + } else { + mwexec("/sbin/sysctl net.link.ether.bridge_ipf=0"); + mwexec("/sbin/sysctl net.link.ether.bridge=0"); + } + + if (!$g['booting']) { + /* reconfigure static routes (kernel may have deleted them) */ + system_routing_configure(); + + /* reload ipfilter (address may have changed) */ + filter_configure(); + + /* reload shaper (address may have changed) */ + shaper_configure(); + + /* reload IPsec tunnels */ + vpn_ipsec_configure(); + + /* reload dhcpd (interface enabled/disabled/bridged status may have changed) */ + services_dhcpd_configure(); + + /* restart dnsmasq */ + services_dnsmasq_configure(); + } + + return 0; +} + +function interfaces_optional_configure_if($opti) { + global $config, $g; + global $bridgeconfig; + + $optcfg = $config['interfaces']['opt' . $opti]; + + if ($g['booting']) { + $optdescr = ""; + if ($optcfg['descr']) + $optdescr = " ({$optcfg['descr']})"; + echo "Configuring OPT{$opti}{$optdescr} interface... "; + } + + if (isset($optcfg['enable'])) { + /* wireless configuration? */ + if (is_array($optcfg['wireless'])) + interfaces_wireless_configure($optcfg['if'], $optcfg['wireless']); + + /* MAC spoofing? */ + if ($optcfg['spoofmac']) + mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . + " link " . escapeshellarg($optcfg['spoofmac'])); + + /* media */ + if ($optcfg['media'] || $optcfg['mediaopt']) { + $cmd = "/sbin/ifconfig " . escapeshellarg($optcfg['if']); + if ($optcfg['media']) + $cmd .= " media " . escapeshellarg($optcfg['media']); + if ($optcfg['mediaopt']) + $cmd .= " mediaopt " . escapeshellarg($optcfg['mediaopt']); + mwexec($cmd); + } + + /* OpenVPN configuration? */ + if (isset($optcfg['ovpn'])) { + if (strstr($if, "tap")) + ovpn_link_tap(); + } + + /* bridged? */ + if ($optcfg['bridge']) { + mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . + " delete up"); + + if ($bridgeconfig != "") + $bridgeconfig .= ","; + + $bridgeconfig .= $optcfg['if'] . ":" . $opti . "," . + $config['interfaces'][$optcfg['bridge']]['if'] . + ":" . $opti; + } else { + mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . " " . + escapeshellarg($optcfg['ipaddr'] . "/" . $optcfg['subnet'])); + } + } else { + mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . + " delete down"); + } + + if ($g['booting']) + echo "done\n"; + + return 0; +} + +function interfaces_wireless_configure($if, $wlcfg) { + global $config, $g; + + /* wireless configuration */ + $ifcargs = escapeshellarg($if) . + " ssid " . escapeshellarg($wlcfg['ssid']) . " channel " . + escapeshellarg($wlcfg['channel']) . " "; + + if ($wlcfg['stationname']) + $ifcargs .= "stationname " . escapeshellarg($wlcfg['stationname']) . " "; + + if (isset($wlcfg['wep']['enable']) && is_array($wlcfg['wep']['key'])) { + $ifcargs .= "wepmode on "; + + $i = 1; + foreach ($wlcfg['wep']['key'] as $wepkey) { + $ifcargs .= "wepkey " . escapeshellarg("{$i}:{$wepkey['value']}") . " "; + if (isset($wepkey['txkey'])) { + $ifcargs .= "weptxkey {$i} "; + } + $i++; + } + } else { + $ifcargs .= "wepmode off "; + } + + switch ($wlcfg['mode']) { + case 'hostap': + if (strstr($if, "wi")) + $ifcargs .= "-mediaopt ibss mediaopt hostap "; + break; + case 'ibss': + case 'IBSS': + if (strstr($if, "wi")) + $ifcargs .= "-mediaopt hostap mediaopt ibss "; + else if (strstr($if, "an")) + $ifcargs .= "mediaopt adhoc "; + break; + case 'bss': + case 'BSS': + if (strstr($if, "wi")) + $ifcargs .= "-mediaopt hostap -mediaopt ibss "; + else if (strstr($if, "an")) + $ifcargs .= "-mediaopt adhoc "; + break; + } + + $ifcargs .= "up"; + + mwexec("/sbin/ifconfig " . $ifcargs); + + return 0; +} + +function interfaces_wan_configure() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + + if ($g['booting']) + echo "Configuring WAN interface... "; + else { + /* kill dhclient */ + killbypid("{$g['varrun_path']}/dhclient.pid"); + + /* kill PPPoE client (mpd) */ + killbypid("{$g['varrun_path']}/mpd.pid"); + + /* wait for processes to die */ + sleep(2); + + /* remove dhclient.conf, if it exists */ + if (file_exists("{$g['varetc_path']}/dhclient.conf")) { + unlink("{$g['varetc_path']}/dhclient.conf"); + } + /* remove mpd.conf, if it exists */ + if (file_exists("{$g['varetc_path']}/mpd.conf")) { + unlink("{$g['varetc_path']}/mpd.conf"); + } + /* remove mpd.links, if it exists */ + if (file_exists("{$g['varetc_path']}/mpd.links")) { + unlink("{$g['varetc_path']}/mpd.links"); + } + /* remove wanip, if it exists */ + if (file_exists("{$g['vardb_path']}/wanip")) { + unlink("{$g['vardb_path']}/wanip"); + } + } + + /* remove all addresses first */ + while (mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " -alias") == 0); + mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " down"); + + /* wireless configuration? */ + if (is_array($wancfg['wireless'])) + interfaces_wireless_configure($wancfg['if'], $wancfg['wireless']); + + if ($wancfg['spoofmac']) + mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . + " link " . escapeshellarg($wancfg['spoofmac'])); + + /* media */ + if ($wancfg['media'] || $wancfg['mediaopt']) { + $cmd = "/sbin/ifconfig " . escapeshellarg($wancfg['if']); + if ($wancfg['media']) + $cmd .= " media " . escapeshellarg($wancfg['media']); + if ($wancfg['mediaopt']) + $cmd .= " mediaopt " . escapeshellarg($wancfg['mediaopt']); + mwexec($cmd); + } + + switch ($wancfg['ipaddr']) { + + case 'dhcp': + interfaces_wan_dhcp_configure(); + break; + + case 'pppoe': + interfaces_wan_pppoe_configure(); + break; + + case 'pptp': + interfaces_wan_pptp_configure(); + break; + + case 'bigpond': + /* just configure DHCP for now; fire up bpalogin when we've got the lease */ + interfaces_wan_dhcp_configure(); + break; + + default: + mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " " . + escapeshellarg($wancfg['ipaddr'] . "/" . $wancfg['subnet'])); + + /* install default route */ + mwexec("/sbin/route delete default"); + mwexec("/sbin/route add default " . escapeshellarg($wancfg['gateway'])); + + /* resync ipfilter (done automatically for DHCP/PPPoE/PPTP) */ + filter_resync(); + } + + if (!$g['booting']) { + /* reconfigure static routes (kernel may have deleted them) */ + system_routing_configure(); + + /* reload ipfilter */ + filter_configure(); + + /* reload shaper */ + shaper_configure(); + + /* reload ipsec tunnels */ + vpn_ipsec_configure(); + + /* restart ez-ipupdate */ + services_dyndns_configure(); + + /* restart dnsmasq */ + services_dnsmasq_configure(); + } + + if ($g['booting']) + echo "done\n"; + + return 0; +} + +function interfaces_wan_dhcp_configure() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + + /* generate dhclient.conf */ + $fd = fopen("{$g['varetc_path']}/dhclient.conf", "w"); + if (!$fd) { + printf("Error: cannot open dhclient.conf in interfaces_wan_dhcp_configure().\n"); + return 1; + } + + $dhclientconf = ""; + + if ($wancfg['dhcphostname']) { + $dhclientconf .= <<<EOD +send dhcp-client-identifier "{$wancfg['dhcphostname']}"; +interface "{$wancfg['if']}" { + send host-name "{$wancfg['dhcphostname']}"; +} + +EOD; + } + + fwrite($fd, $dhclientconf); + fclose($fd); + + /* fire up dhclient - don't wait for the lease (-nw) */ + mwexec("/sbin/dhclient -nw -cf {$g['varetc_path']}/dhclient.conf " . + escapeshellarg($wancfg['if']) . " &"); + + return 0; +} + +function interfaces_wan_pppoe_configure() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + $pppoecfg = $config['pppoe']; + + /* generate mpd.conf */ + $fd = fopen("{$g['varetc_path']}/mpd.conf", "w"); + if (!$fd) { + printf("Error: cannot open mpd.conf in interfaces_wan_pppoe_configure().\n"); + return 1; + } + + $idle = 0; + + if (isset($pppoecfg['ondemand'])) { + $ondemand = "enable"; + if ($pppoecfg['timeout']) + $idle = $pppoecfg['timeout']; + } else { + $ondemand = "disable"; + } + + $mpdconf = <<<EOD +pppoe: + new -i ng0 pppoe pppoe + set iface route default + set iface {$ondemand} on-demand + set iface idle {$idle} + set iface up-script /usr/local/sbin/ppp-linkup + +EOD; + + if (isset($pppoecfg['ondemand'])) { + $mpdconf .= <<<EOD + set iface addrs 10.0.0.1 10.0.0.2 + +EOD; + } + + $mpdconf .= <<<EOD + set bundle disable multilink + set bundle authname "{$pppoecfg['username']}" + set bundle password "{$pppoecfg['password']}" + set link keep-alive 10 60 + set link max-redial 0 + set link no acfcomp protocomp + set link disable pap chap + set link accept chap + set link mtu 1492 + set ipcp yes vjcomp + set ipcp ranges 0.0.0.0/0 0.0.0.0/0 + set ipcp enable req-pri-dns + set ipcp enable req-sec-dns + open iface + +EOD; + + fwrite($fd, $mpdconf); + fclose($fd); + + /* generate mpd.links */ + $fd = fopen("{$g['varetc_path']}/mpd.links", "w"); + if (!$fd) { + printf("Error: cannot open mpd.links in interfaces_wan_pppoe_configure().\n"); + return 1; + } + + $mpdconf = <<<EOD +pppoe: + set link type pppoe + set pppoe iface {$wancfg['if']} + set pppoe service "{$pppoecfg['provider']}" + set pppoe enable originate + set pppoe disable incoming + +EOD; + + fwrite($fd, $mpdconf); + fclose($fd); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd -b -d {$g['varetc_path']} -p {$g['varrun_path']}/mpd.pid pppoe"); + + return 0; +} + +function interfaces_wan_pptp_configure() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + $pptpcfg = $config['pptp']; + + /* generate mpd.conf */ + $fd = fopen("{$g['varetc_path']}/mpd.conf", "w"); + if (!$fd) { + printf("Error: cannot open mpd.conf in interfaces_wan_pptp_configure().\n"); + return 1; + } + + $idle = 0; + + if (isset($pptpcfg['ondemand'])) { + $ondemand = "enable"; + if ($pptpcfg['timeout']) + $idle = $pptpcfg['timeout']; + } else { + $ondemand = "disable"; + } + + $mpdconf = <<<EOD +pptp: + new -i ng0 pptp pptp + set iface route default + set iface {$ondemand} on-demand + set iface idle {$idle} + set iface up-script /usr/local/sbin/ppp-linkup + +EOD; + + if (isset($pptpcfg['ondemand'])) { + $mpdconf .= <<<EOD + set iface addrs {$pptpcfg['local']} {$pptpcfg['remote']} + +EOD; + } + + $mpdconf .= <<<EOD + set bundle disable multilink + set bundle authname "{$pptpcfg['username']}" + set bundle password "{$pptpcfg['password']}" + set link keep-alive 10 60 + set link max-redial 0 + set link no acfcomp protocomp + set link disable pap chap + set link accept chap + set ipcp no vjcomp + set ipcp ranges 0.0.0.0/0 0.0.0.0/0 + set ipcp enable req-pri-dns + set ipcp enable req-sec-dns + open + +EOD; + + fwrite($fd, $mpdconf); + fclose($fd); + + /* generate mpd.links */ + $fd = fopen("{$g['varetc_path']}/mpd.links", "w"); + if (!$fd) { + printf("Error: cannot open mpd.links in interfaces_wan_pptp_configure().\n"); + return 1; + } + + $mpdconf = <<<EOD +pptp: + set link type pptp + set pptp enable originate outcall + set pptp disable windowing + set pptp self {$pptpcfg['local']} + set pptp peer {$pptpcfg['remote']} + +EOD; + + fwrite($fd, $mpdconf); + fclose($fd); + + /* configure interface */ + mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " " . + escapeshellarg($pptpcfg['local'] . "/" . $pptpcfg['subnet'])); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd -b -d {$g['varetc_path']} -p {$g['varrun_path']}/mpd.pid pptp"); + + return 0; +} + +function interfaces_wan_bigpond_configure($curwanip) { + global $config, $g; + + $bpcfg = $config['bigpond']; + + if (!$curwanip) { + /* IP address not configured yet, exit */ + return 0; + } + + /* kill bpalogin */ + killbyname("bpalogin"); + + /* wait a moment */ + sleep(1); + + /* get the default domain */ + $nfd = @fopen("{$g['varetc_path']}/defaultdomain.conf", "r"); + if ($nfd) { + $defaultdomain = trim(fgets($nfd)); + fclose($nfd); + } + + /* generate bpalogin.conf */ + $fd = fopen("{$g['varetc_path']}/bpalogin.conf", "w"); + if (!$fd) { + printf("Error: cannot open bpalogin.conf in interfaces_wan_bigpond_configure().\n"); + return 1; + } + + if (!$bpcfg['authserver']) + $bpcfg['authserver'] = "dce-server"; + if (!$bpcfg['authdomain']) + $bpcfg['authdomain'] = $defaultdomain; + + $bpconf = <<<EOD +username {$bpcfg['username']} +password {$bpcfg['password']} +authserver {$bpcfg['authserver']} +authdomain {$bpcfg['authdomain']} +localport 5050 + +EOD; + + if ($bpcfg['minheartbeatinterval']) + $bpconf .= "minheartbeatinterval {$bpcfg['minheartbeatinterval']}\n"; + + fwrite($fd, $bpconf); + fclose($fd); + + /* fire up bpalogin */ + mwexec("/usr/local/sbin/bpalogin -c {$g['varetc_path']}/bpalogin.conf"); + + return 0; +} + +function get_real_wan_interface() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + + $wanif = $wancfg['if']; + if (($wancfg['ipaddr'] == "pppoe") || ($wancfg['ipaddr'] == "pptp")) { + $wanif = $g['pppoe_interface']; + } + + return $wanif; +} + +function get_current_wan_address() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + + if (in_array($wancfg['ipaddr'], array('pppoe','dhcp','pptp','bigpond'))) { + /* dynamic WAN IP address, find out which one */ + $wanif = get_real_wan_interface(); + + /* get interface info with netstat */ + exec("/usr/bin/netstat -nWI " . escapeshellarg($wanif) . " -f inet", $ifinfo); + + if (isset($ifinfo[1])) { + $aif = preg_split("/\s+/", $ifinfo[1]); + $curwanip = chop($aif[3]); + + if ($curwanip && is_ipaddr($curwanip) && ($curwanip != "0.0.0.0")) + return $curwanip; + } + + return null; + } else { + /* static WAN IP address */ + return $wancfg['ipaddr']; + } +} + +?> diff --git a/etc/inc/openvpn.inc b/etc/inc/openvpn.inc new file mode 100644 index 0000000..2414ae0 --- /dev/null +++ b/etc/inc/openvpn.inc @@ -0,0 +1,559 @@ +<?php +/* + openvpn.inc + + Copyright (C) 2004 Peter Curran (peter@closeconsultants.com). + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("globals.inc"); +require_once("config.inc"); +require_once("functions.inc"); + +function ovpn_configure() { + global $config; + if (is_array($config['ovpn']['server'])) + ovpn_config_server(); + if (is_array($config['ovpn']['client'])) + ovpn_config_client(); + return; +} + +function ovpn_link_tap() { + /* Add a reference to the tap KLM. If ref count = 1, load it */ + global $g; + + if (!is_file($g['vardb_path'] ."/ovpn_tap_link")){ + $link_count = 1; + mwexec("/sbin/kldload if_tap"); + $fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'w'); + } + else { + $fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'r+'); + $link_count = fread($fd); + $link_count ++; + } + fwrite($fd, $link_count); + fclose($fd); + return true; +} + +function ovpn_unlink_tap() { + /* Remove a reference to the tap KLM. If ref count = 0, unload it */ + global $g; + + if (!is_file($g['vardb_path'] ."/ovpn_tap_link")) + return false; //no file, no links so why are we called? + + $fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'r+'); + $link_count = fread($fd); + $link_count --; + fwrite($fd, $link_count); + fclose($fd); + + if ($link_count == 0) + mwexec("/sbin/kldunload if_tap"); + return true; +} + +/*****************************/ +/* Server-related functions */ + +/* Configure the server */ +function ovpn_config_server() { + global $config, $g; + + if (isset($config['ovpn']['server']['enable'])) { + + if ($g['booting']) + echo "Starting OpenVPN server... "; + + /* kill any running openvpn daemon */ + killbypid($g['varrun_path']."/ovpn_srv.pid"); + + /* Remove old certs & keys */ + unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_srv_cert.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_srv_key.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_dh.pem"); + + /* Copy the TLS-Server certs & keys to disk */ + $fd = @fopen("{$g['vardb_path']}/ovpn_ca_cert.pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($config['ovpn']['server']['ca_cert'])."\n"); + fclose($fd); + } + $fd = @fopen("{$g['vardb_path']}/ovpn_srv_cert.pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($config['ovpn']['server']['srv_cert'])."\n"); + fclose($fd); + } + $fd = @fopen("{$g['vardb_path']}/ovpn_srv_key.pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($config['ovpn']['server']['srv_key'])."\n"); + fclose($fd); + } + $fd = @fopen("{$g['vardb_path']}/ovpn_dh.pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($config['ovpn']['server']['dh_param'])."\n"); + fclose($fd); + } + + /* Start the openvpn daemon */ + mwexec("/usr/local/sbin/openvpn " . ovpn_srv_config_generate()); + + if ($g['booting']) + /* Send the boot message */ + echo "done\n"; + } + else { + if (!$g['booting']){ + /* stop any processes, unload the tap module */ + /* Remove old certs & keys */ + unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_srv_cert.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_srv_key.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_dh.pem"); + killbypid("{$g['varrun_path']}/ovpn_srv.pid"); + if ($config['ovpn']['server']['tun_iface'] == 'tap0') + ovpn_unlink_tap(); + } + } + return 0; +} + +/* Generate the config for a OpenVPN server */ +function ovpn_srv_config_generate() { + global $config, $g; + $server = $config['ovpn']['server']; + + /* First the generic stuff: + - We are a server + - We are a TLS Server (for authentication) + - We will run without privilege + */ + $ovpn_config = "--daemon --user nobody --group nobody --verb {$server['verb']} "; + + /* pid file */ + $ovpn_config .= "--writepid {$g['varrun_path']}/ovpn_srv.pid "; + + /* interface */ + $ovpn_config .= "--dev {$server['tun_iface']} "; + + /* port */ + $ovpn_config .= "--port {$server['port']} "; + + /* Interface binding - 1 or all */ + if ($server['bind_iface'] != 'all') { + if ($ipaddr = ovpn_get_ip($server['bind_iface'])) + $ovpn_config .= "--local $ipaddr "; + else + return "Interface bridged"; + + } + + /* Client to client routing (off by default) */ + if (isset($server['cli2cli'])) + $ovpn_config .= "--client-to-client "; + + /* Set maximum simultaneous clients */ + $ovpn_config .= "--max-clients {$server['maxcli']} "; + + /* New --server macro simplifies config */ + $mask = ovpn_calc_mask($server['prefix']); + $ovpn_config .= "--server {$server['ipblock']} {$mask} "; + + /* TLS-Server params */ + $ovpn_config .= "--ca {$g['vardb_path']}/ovpn_ca_cert.pem "; + $ovpn_config .= "--cert {$g['vardb_path']}/ovpn_srv_cert.pem "; + $ovpn_config .= "--key {$g['vardb_path']}/ovpn_srv_key.pem "; + $ovpn_config .= "--dh {$g['vardb_path']}/ovpn_dh.pem "; + + /* Data channel encryption cipher*/ + $ovpn_config .= "--cipher {$server['crypto']} "; + + /* Duplicate CNs */ + if (isset($server['dupcn'])) + $ovpn_config .= "--duplicate-cn "; + + /* Client push - redirect gateway */ + if (isset($server['psh_options']['redir'])){ + if (isset($server['psh_options']['redir_loc'])) + $ovpn_config .= "--push \"redirect-gateway 'local'\" "; + else + $ovpn_config .= "--push \"redirect-gateway\" "; + } + + /* Client push - route delay */ + if (isset($server['psh_options']['rte_delay'])) + $ovpn_config .= "--push \"route-delay {$server['psh_options']['rte_delay']}\" "; + + /* Client push - ping (note we set both server and client) */ + if (isset ($server['psh_options']['ping'])){ + $ovpn_config .= "--ping {$server['psh_options']['ping']} "; + $ovpn_config .= "--push \"ping {$server['psh_options']['ping']}\" "; + } + + /* Client push - ping-restart (note server uses 2 x client interval) */ + if (isset ($server['psh_options']['pingrst'])){ + $interval = $server['psh_options']['pingrst']; + $ovpn_config .= "--ping-restart " . ($interval * 2) . " "; + $ovpn_config .= "--push \"ping-restart $interval\" "; + } + + /* Client push - ping-exit (set on client) */ + if (isset ($server['psh_options']['pingexit'])){ + $ovpn_config .= "--ping-exit {$server['psh_options']['pingexit']} "; + $ovpn_config .= "--push \"ping-exit {$server['psh_options']['pingexit']}\" "; + } + + /* Client push - inactive (set on client) */ + if (isset ($server['psh_options']['inact'])){ + $ovpn_config .= "--inactive {$server['psh_options']['pingexit']} "; + $ovpn_config .= "--push \"inactive {$server['psh_options']['inact']}\" "; + } + + //trigger_error("OVPN: $ovpn_config", E_USER_NOTICE); + return $ovpn_config; +} + +/* Define an OVPN Server tunnel interface in the interfaces array and assign a name */ +function ovpn_server_iface(){ + global $config, $g; + + $i = 1; + while (true) { + $ifname = 'opt' . $i; + if (is_array($config['interfaces'][$ifname])) { + if ((isset($config['interfaces'][$ifname]['ovpn'])) + && ($config['interfaces'][$ifname]['ovpn'] == 'server')) + /* Already an interface defined - overwrite */ + break; + } + else { + /* No existing entry, this is first unused */ + $config['interfaces'][$ifname] = array(); + break; + } + $i++; + } + $config['interfaces'][$ifname]['descr'] = "OVPN server"; + $config['interfaces'][$ifname]['if'] = $config['ovpn']['server']['tun_iface']; + $config['interfaces'][$ifname]['ipaddr'] = long2ip( ip2long($config['ovpn']['server']['ipblock']) + 1); + $config['interfaces'][$ifname]['subnet'] = $config['ovpn']['server']['prefix']; + $config['interfaces'][$ifname]['enable'] = isset($config['ovpn']['server']['enable']) ? true : false; + $config['interfaces'][$ifname]['ovpn'] = 'server'; + + write_config(); + + return "OpenVPN server interface defined"; +} + +/********************************************************/ +/* Client related functions */ +function ovpn_config_client() { + /* Boot time configuration */ + global $config, $g; + + foreach ($config['ovpn']['client']['tunnel'] as $id => $client) { + if (isset($client['enable'])) { + + if ($g['booting']) + echo "Starting OpenVPN client $id... "; + + /* kill any running openvpn daemon */ + killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid"); + + /* Remove old certs & keys */ + unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_cli_cert_{$id}.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_cli_key_{$id}.pem"); + + /* Copy the TLS-Client certs & keys to disk */ + /*$fd = @fopen("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem", "w");*/ + $fd = fopen("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($client['ca_cert'])."\n"); + fclose($fd); + } + else + trigger_error("OVPN: No open for CA", E_USER_NOTICE); + $fd = fopen($g['vardb_path']."/ovpn_cli_cert_".$id.".pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($client['cli_cert'])."\n"); + fclose($fd); + } + $fd = fopen($g['vardb_path']."/ovpn_cli_key_".$id.".pem", "w"); + if ($fd) { + fwrite($fd, base64_decode($client['cli_key'])."\n"); + fclose($fd); + } + + /* Start openvpn for this client */ + mwexec("/usr/local/sbin/openvpn " . ovpn_cli_config_generate($id)); + + if ($g['booting']) + /* Send the boot message */ + echo "done\n"; + } + else { + if (!$g['booting']){ + /* stop any processes, unload the tap module */ + /* Remove old certs & keys */ + unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_cli_cert_{$id}.pem"); + unlink_if_exists("{$g['vardb_path']}/ovpn_cli_key_{$id}.pem"); + killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid"); + if ($client['type'] == "tap") + ovpn_unlink_tap(); + } + } + } + return 0; + +} + +/* Kill off a running client process */ +function ovpn_client_kill($id) { + global $g; + + killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid"); + return 0; +} + +function ovpn_cli_config_generate($id) { + /* configure the named client */ + global $config, $g; + $client = $config['ovpn']['client']['tunnel']; + + /* Client support in 2.0 is very simple */ + + $ovpn_config = "--client --daemon --verb 1 "; + + /* pid file */ + $ovpn_config .= "--writepid {$g['varrun_path']}/ovpn_client{$id}.pid "; + + /* interface */ + $ovpn_config .= "--dev {$client[$id]['if']} "; + + /* protocol */ + $ovpn_config .= "--proto {$client[$id]['proto']} "; + + /* port */ + $ovpn_config .= "--lport {$client[$id]['cport']} "; + + /* server location */ + $ovpn_config .= "--remote {$client[$id]['saddr']} {$client[$id]['sport']} "; + + /* TLS-Server params */ + $ovpn_config .= "--ca {$g['vardb_path']}/ovpn_ca_cert_{$id}.pem "; + $ovpn_config .= "--cert {$g['vardb_path']}/ovpn_cli_cert_{$id}.pem "; + $ovpn_config .= "--key {$g['vardb_path']}/ovpn_cli_key_{$id}.pem "; + + /* Data channel encryption cipher*/ + $ovpn_config .= "--cipher {$client[$id]['crypto']} "; + + //trigger_error("OVPN: $ovpn_config", E_USER_NOTICE); + return $ovpn_config; +} + +/* Define an OVPN tunnel interface in the interfaces array for each client */ +function ovpn_client_iface(){ + global $config; + + foreach ($config['ovpn']['client']['tunnel'] as $id => $client) { + if (isset($client['enable'])) { + $i = 1; + while (true) { + $ifname = 'opt' . $i; + if (is_array($config['interfaces'][$ifname])) { + if ((isset($config['interfaces'][$ifname]['ovpn'])) + && ($config['interfaces'][$ifname]['ovpn'] == "client{$id}")) + /* Already an interface defined - overwrite */ + break; + } + else { + /* No existing entry, this is first unused */ + $config['interfaces'][$ifname] = array(); + break; + } + $i++; + } + if (isset($client['descr'])) + $config['interfaces'][$ifname]['descr'] = $client['descr']; + else + $config['interfaces'][$ifname]['descr'] = "OVPN client-{$id}"; + $config['interfaces'][$ifname]['if'] = $client['if']; + $config['interfaces'][$ifname]['ipaddr'] = "0.0.0.0"; + $config['interfaces'][$ifname]['subnet'] = "0"; + $config['interfaces'][$ifname]['enable'] = isset($client['enable']) ? true : false; + $config['interfaces'][$ifname]['ovpn'] = "client{$id}"; + write_config(); + } + } + return "OpenVPN client interfaces defined"; +} + +/* Delete a client interface definition */ +function ovpn_client_iface_del($id) { + global $config; + + $i = 1; + while (true) { + $ifname = 'opt' . $i; + if (is_array($config['interfaces'][$ifname])) { + if ((isset($config['interfaces'][$ifname]['ovpn'])) + && ($config['interfaces'][$ifname]['ovpn'] == "client{$id}")) + unset($config['interfaces'][$ifname]); + } + } +} + +/******************/ +/* Misc functions */ + +/* Calculate the last address in a range given the start and /prefix */ +function ovpn_calc_end($start, $prefix){ + + $first = ip2long($start); + $last = pow(2,(32 - $prefix)) - 1 + $first; + return long2ip($last); +} + +/* Calculate a mask given a /prefix */ +function ovpn_calc_mask($prefix){ + + return long2ip(ip2long("255.255.255.255") - (pow( 2, (32 - $prefix)) - 1)); +} + +/* Read in a file from the $_FILES array */ +function ovpn_get_file($file){ + global $g; + + if (!is_uploaded_file($_FILES[$file]['tmp_name'])){ + trigger_error("Bad file upload".$_FILES[$file]['error'], E_USER_NOTICE); + return NULL; + } + $contents = file_get_contents($_FILES[$file]['tmp_name']); + return $contents; +} + + +/* Get the IP address of a specified interface */ +function ovpn_get_ip($iface){ + global $config; + + if ($iface == 'wan') + return get_current_wan_address(); + + if ($config['interfaces'][$iface]['bridge']) + /* No bridging (yet) */ + return false; + return $config['interfaces'][$iface]['ipaddr']; +} + +/* Get a list of the cipher options supported by OpenVPN */ +function ovpn_get_cipher_list(){ + +/* exec("/usr/local/sbin/openvpn --show-ciphers", $raw); + print_r ($raw); + + $ciphers = preg_grep('/ bit default key /', $raw); + + for($i = 0; $i <count($ciphers); $i++){ + $tmp = explode(' ',$ciphers[$i]); + $cipher_list["$tmp[0]"] = "{$tmp[0]} ({$tmp[1]} {$tmp[2]})"; + } +*/ + $cipher_list = array('DES-CBC' => 'DES-CBC (64 bit)', + 'RC2-CBC' => 'RC2-CBC (128 bit)', + 'DES-EDE-CBC' => 'DES-EDE-CBC (128 bit)', + 'DES-EDE3-CBC' => 'DES-EDE3-CBC (192 bit)', + 'DESX-CBC' => 'DESX-CBC (192 bit)', + 'BF-CBC' => 'BF-CBC (128 bit)', + 'RC2-40-CBC' => 'RC2-40-CBC (40 bit)', + 'CAST5-CBC' => 'CAST5-CBC (128 bit)', + 'RC5-CBC' => 'RC5-CBC (128 bit)', + 'RC2-64-CBC' => 'RC2-64-CBC (64 bit)', + 'AES-128-CBC' => 'AES-128-CBC (128 bit)', + 'AES-192-CBC' => 'AES-192-CBC (192 bit)', + 'AES-256-CBC' => 'AES-256-CBC (256 bit)'); + return $cipher_list; +} + + +/* Build a list of the current real interfaces */ +function ovpn_real_interface_list(){ + global $config; + + $interfaces = array('all' => 'ALL', + 'lan' => 'LAN', + 'wan' => 'WAN'); + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) { + if (isset($config['interfaces']['opt' . $i]['ovpn'])) + /* Hide our own interface */ + break; + if (isset($config['interfaces']['opt' . $i]['enable'])) + $interfaces['opt' . $i] = $config['interfaces']['opt' . $i]['descr']; + } + return $interfaces; +} + + +/* lock openvpn information, decide that the lock file is stale after + 10 seconds */ +function ovpn_lock() { + + global $g; + + $lockfile = "{$g['varrun_path']}/ovpn.lock"; + + $n = 0; + while ($n < 10) { + /* open the lock file in append mode to avoid race condition */ + if ($fd = @fopen($lockfile, "x")) { + /* succeeded */ + fclose($fd); + return; + } else { + /* file locked, wait and try again */ + sleep(1); + $n++; + } + } +} + +/* unlock configuration file */ +function ovpn_unlock() { + + global $g; + + $lockfile = "{$g['varrun_path']}/ovpn.lock"; + + if (file_exists($lockfile)) + unlink($lockfile); +} + +?> diff --git a/etc/inc/services.inc b/etc/inc/services.inc new file mode 100644 index 0000000..17bc959 --- /dev/null +++ b/etc/inc/services.inc @@ -0,0 +1,440 @@ +<?php +/* + services.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); + +function services_dhcpd_configure() { + global $config, $g; + + /* kill any running dhcpd */ + killbypid("{$g['varrun_path']}/dhcpd.pid"); + + $syscfg = $config['system']; + $dhcpdcfg = $config['dhcpd']; + + /* DHCP enabled on any interfaces? */ + $dhcpdenable = false; + foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) { + if (isset($dhcpifconf['enable']) && + (($dhcpif == "lan") || + (isset($config['interfaces'][$dhcpif]['enable']) && + $config['interfaces'][$dhcpif]['if'] && (!$config['interfaces'][$dhcpif]['bridge'])))) + $dhcpdenable = true; + } + + if (!$dhcpdenable) + return 0; + + if ($g['booting']) + echo "Starting DHCP service... "; + else + sleep(1); + + /* write dhcpd.conf */ + $fd = fopen("{$g['varetc_path']}/dhcpd.conf", "w"); + if (!$fd) { + printf("Error: cannot open dhcpd.conf in services_dhcpd_configure().\n"); + return 1; + } + + $dhcpdconf = <<<EOD +option domain-name "{$syscfg['domain']}"; +default-lease-time 7200; +max-lease-time 86400; +authoritative; +log-facility local7; +ddns-update-style none; + +EOD; + + $dhcpdifs = array(); + foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) { + + $ifcfg = $config['interfaces'][$dhcpif]; + + if (!isset($dhcpifconf['enable']) || + (($dhcpif != "lan") && + (!isset($ifcfg['enable']) || !$ifcfg['if'] || $ifcfg['bridge']))) + continue; + + $subnet = gen_subnet($ifcfg['ipaddr'], $ifcfg['subnet']); + $subnetmask = gen_subnet_mask($ifcfg['subnet']); + + $dnscfg = ""; + + if ($dhcpifconf['domain']) { + $dnscfg .= " option domain-name \"{$dhcpifconf['domain']}\";\n"; + } + + if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) { + $dnscfg .= " option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";"; + } else if (isset($config['dnsmasq']['enable'])) { + $dnscfg .= " option domain-name-servers " . $ifcfg['ipaddr'] . ";"; + } else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $dnscfg .= " option domain-name-servers " . join(",", $syscfg['dnsserver']) . ";"; + } + + $dhcpdconf .= "subnet $subnet netmask $subnetmask {\n"; + $dhcpdconf .= " pool {\n"; + if (isset($dhcpifconf['denyunknown'])) + $dhcpdconf .= " deny unknown clients;\n"; + + if ($dhcpifconf['gateway']) + $routers = $dhcpifconf['gateway']; + else + $routers = $ifcfg['ipaddr']; + + $dhcpdconf .= <<<EOD + range {$dhcpifconf['range']['from']} {$dhcpifconf['range']['to']}; + } + option routers {$routers}; +$dnscfg + +EOD; + + if ($dhcpifconf['defaultleasetime']) + $dhcpdconf .= " default-lease-time {$dhcpifconf['defaultleasetime']};\n"; + if ($dhcpifconf['maxleasetime']) + $dhcpdconf .= " max-lease-time {$dhcpifconf['maxleasetime']};\n"; + + if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) { + $dhcpdconf .= " option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n"; + $dhcpdconf .= " option netbios-node-type 8;\n"; + } + + if ($dhcpifconf['next-server']) + $dhcpdconf .= " next-server {$dhcpifconf['next-server']};\n"; + if ($dhcpifconf['filename']) + $dhcpdconf .= " filename \"{$dhcpifconf['filename']}\";\n"; + + $dhcpdconf .= <<<EOD +} + +EOD; + + /* add static mappings */ + if (is_array($dhcpifconf['staticmap'])) { + + $i = 0; + foreach ($dhcpifconf['staticmap'] as $sm) { + $dhcpdconf .= <<<EOD +host s_{$dhcpif}_{$i} { + hardware ethernet {$sm['mac']}; + +EOD; + if ($sm['ipaddr']) + $dhcpdconf .= " fixed-address {$sm['ipaddr']};\n"; + + $dhcpdconf .= "}\n"; + $i++; + } + } + + $dhcpdifs[] = $ifcfg['if']; + } + + fwrite($fd, $dhcpdconf); + fclose($fd); + + /* create an empty leases database */ + touch("{$g['vardb_path']}/dhcpd.leases"); + + /* fire up dhcpd */ + mwexec("/usr/local/sbin/dhcpd -cf {$g['varetc_path']}/dhcpd.conf " . + join(" ", $dhcpdifs)); + + if (!$g['booting']) { + filter_configure(); + } else + echo "done\n"; + + return 0; +} + +function services_dhcrelay_configure() { + global $config, $g; + + /* kill any running dhcrelay */ + killbypid("{$g['varrun_path']}/dhcrelay.pid"); + + $dhcrelaycfg = $config['dhcrelay']; + + /* DHCPRelay enabled on any interfaces? */ + $dhcrelayenable = false; + foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) { + if (isset($dhcrelayifconf['enable']) && + (($dhcrelayif == "lan") || + (isset($config['interfaces'][$dhcrelayif]['enable']) && + $config['interfaces'][$dhcrelayif]['if'] && (!$config['interfaces'][$dhcrelayif]['bridge'])))) + $dhcrelayenable = true; + } + + if (!$dhcrelayenable) + return 0; + + if ($g['booting']) + echo "Starting DHCP relay service... "; + else + sleep(1); + + $dhcrelayifs = array(); + foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) { + + $ifcfg = $config['interfaces'][$dhcrelayif]; + + if (!isset($dhcrelayifconf['enable']) || + (($dhcrelayif != "lan") && + (!isset($ifcfg['enable']) || !$ifcfg['if'] || $ifcfg['bridge']))) + continue; + + $dhcrelayifs[] = $ifcfg['if']; + } + + /* In order for the relay to work, it needs to be active on the + interface in which the destination server sits */ + foreach ($config['interfaces'] as $ifname) { + $subnet = $ifname['ipaddr'] . "/" . $ifname['subnet']; + if (ip_in_subnet($dhcrelaycfg['server'],$subnet)) + $destif = $ifname['if']; + } + + if (!isset($destif)) + $destif = $config['interfaces']['wan']['if']; + + $dhcrelayifs[] = $destif; + $dhcrelayifs = array_unique($dhcrelayifs); + + /* fire up dhcrelay */ + $cmd = "/usr/local/sbin/dhcrelay -i " . join(" -i ", $dhcrelayifs); + + if (isset($dhcrelaycfg['agentoption'])) + $cmd .= " -a -m replace"; + + $cmd .= " {$dhcrelaycfg['server']}"; + mwexec($cmd); + + if (!$g['booting']) { + filter_configure(); + } else + echo "done\n"; + + return 0; +} + +function services_dyndns_reset() { + global $config, $g; + + if (file_exists("{$g['vardb_path']}/ez-ipupdate.cache")) { + unlink("{$g['vardb_path']}/ez-ipupdate.cache"); + } + + if (file_exists("{$g['conf_path']}/ez-ipupdate.cache")) { + conf_mount_rw(); + unlink("{$g['conf_path']}/ez-ipupdate.cache"); + conf_mount_ro(); + } + + return 0; +} + +function services_dyndns_configure() { + global $config, $g; + + /* kill any running ez-ipupdate */ + /* ez-ipupdate needs SIGQUIT instead of SIGTERM */ + sigkillbypid("{$g['varrun_path']}/ez-ipupdate.pid", "QUIT"); + + $dyndnscfg = $config['dyndns']; + $wancfg = $config['interfaces']['wan']; + + if (isset($dyndnscfg['enable'])) { + + if ($g['booting']) + echo "Starting DynDNS client... "; + else + sleep(1); + + /* determine WAN interface name */ + $wanif = get_real_wan_interface(); + + /* write ez-ipupdate.conf */ + $fd = fopen("{$g['varetc_path']}/ez-ipupdate.conf", "w"); + if (!$fd) { + printf("Error: cannot open ez-ipupdate.conf in services_dyndns_configure().\n"); + return 1; + } + + $ezipupdateconf = <<<EOD +service-type={$dyndnscfg['type']} +user={$dyndnscfg['username']}:{$dyndnscfg['password']} +host={$dyndnscfg['host']} +interface=$wanif +max-interval=2073600 +pid-file={$g['varrun_path']}/ez-ipupdate.pid +cache-file={$g['vardb_path']}/ez-ipupdate.cache +execute=/etc/rc.dyndns.storecache +daemon + +EOD; + + /* enable MX? */ + if ($dyndnscfg['mx']) { + $ezipupdateconf .= "mx={$dyndnscfg['mx']}\n"; + } + + /* enable wildcards? */ + if (isset($dyndnscfg['wildcard'])) { + $ezipupdateconf .= "wildcard\n"; + } + + fwrite($fd, $ezipupdateconf); + fclose($fd); + + /* if we're booting, copy the cache file from /conf */ + if ($g['booting']) { + if (file_exists("{$g['conf_path']}/ez-ipupdate.cache")) { + copy("{$g['conf_path']}/ez-ipupdate.cache", "{$g['vardb_path']}/ez-ipupdate.cache"); + } + } + + /* run ez-ipupdate */ + mwexec("/usr/local/bin/ez-ipupdate -c {$g['varetc_path']}/ez-ipupdate.conf"); + + if ($g['booting']) + echo "done\n"; + } + + return 0; +} + +function services_dnsmasq_configure() { + global $config, $g; + + /* kill any running dnsmasq */ + sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM"); + + if (isset($config['dnsmasq']['enable'])) { + + if ($g['booting']) + echo "Starting DNS forwarder... "; + else + sleep(1); + + /* generate hosts file */ + system_hosts_generate(); + + $args = ""; + + if (isset($config['dnsmasq']['regdhcp'])) { + + $args .= " -l {$g['vardb_path']}/dhcpd.leases" . + " -s {$config['system']['domain']}"; + } + + /* run dnsmasq */ + mwexec("/usr/local/sbin/dnsmasq {$args}"); + + if ($g['booting']) + echo "done\n"; + } + + if (!$g['booting']) { + services_dhcpd_configure(); + } + + return 0; +} + +function services_snmpd_configure() { + global $config, $g; + + /* kill any running snmpd */ + sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM"); + + if (isset($config['snmpd']['enable'])) { + + if ($g['booting']) + echo "Starting SNMP agent... "; + + /* generate snmpd.conf */ + $fd = fopen("{$g['varetc_path']}/snmpd.conf", "w"); + if (!$fd) { + printf("Error: cannot open snmpd.conf in services_snmpd_configure().\n"); + return 1; + } + + $snmpdconf = <<<EOD +syslocation "{$config['snmpd']['syslocation']}" +syscontact "{$config['snmpd']['syscontact']}" +rocommunity "{$config['snmpd']['rocommunity']}" + +EOD; + + fwrite($fd, $snmpdconf); + fclose($fd); + + /* run snmpd */ + mwexec("/usr/local/sbin/snmpd -c {$g['varetc_path']}/snmpd.conf" . + " -P {$g['varrun_path']}/snmpd.pid"); + + if ($g['booting']) + echo "done\n"; + } + + return 0; +} + +function services_proxyarp_configure() { + global $config, $g; + + /* kill any running choparp */ + killbyname("choparp"); + + if (is_array($config['proxyarp']) && count($config['proxyarp']) && + (is_ipaddr($config['interfaces']['wan']['ipaddr']) || + ($config['interfaces']['wan']['ipaddr'] == "dhcp") || + ($config['interfaces']['wan']['ipaddr'] == "bigpond"))) { + + $args = $config['interfaces']['wan']['if'] . " auto"; + + foreach ($config['proxyarp']['proxyarpnet'] as $paent) { + if (isset($paent['network'])) + $args .= " " . escapeshellarg($paent['network']); + else if (isset($paent['range'])) + $args .= " " . escapeshellarg($paent['range']['from'] . "-" . + $paent['range']['to']); + } + + mwexec_bg("/usr/local/sbin/choparp " . $args); + } +} + +?> diff --git a/etc/inc/shaper.inc b/etc/inc/shaper.inc new file mode 100644 index 0000000..71e0575 --- /dev/null +++ b/etc/inc/shaper.inc @@ -0,0 +1,403 @@ +<?php +/* + shaper.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); + +function shaper_configure() { + global $config, $g; + + if (isset($config['pfqueueing']['enable'])) { + + if ($g['booting']) + echo "Starting traffic shaper... "; + + /* generate shaper rules */ + $shaperrules = shaper_rules_generate(); + + /* make sure ipfw and dummynet are loaded */ + mwexec("/sbin/kldload ipfw"); + mwexec("/sbin/kldload dummynet"); + + /* change one_pass to 1 so ipfw stops checking after + a rule has matched */ + mwexec("/sbin/sysctl net.inet.ip.fw.one_pass=1"); + + /* load shaper rules */ + mwexec("/sbin/ipfw -f delete set 4"); + mwexec("/sbin/ipfw -f pipe flush"); + + /* XXX - seems like ipfw cannot accept rules directly on stdin, + so we have to write them to a temporary file first */ + $fd = fopen("{$g['tmp_path']}/ipfw.rules", "w"); + if (!$fd) { + printf("Cannot open ipfw.rules in shaper_configure()\n"); + return 1; + } + + fwrite($fd, $shaperrules); + fclose($fd); + + mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.rules"); + + unlink("{$g['tmp_path']}/ipfw.rules"); + + /* make sure bridged packets are shaped as well */ + mwexec("/sbin/sysctl net.link.ether.bridge_ipfw=1"); + + if ($g['booting']) + echo "done\n"; + + } else { + mwexec("/sbin/sysctl net.link.ether.bridge_ipfw=0"); + if (!isset($config['captiveportal']['enable'])) { + /* unload ipfw and dummynet */ + #mwexec("/sbin/kldunload dummynet"); + #mwexec("/sbin/kldunload ipfw"); + } else { + /* captive portal is on - just remove our rules */ + mwexec("/sbin/ipfw -f delete set 4"); + mwexec("/sbin/ipfw -f pipe flush"); + } + } + + return 0; +} + +function shaper_rules_generate() { + global $config, $g; + + $wancfg = $config['interfaces']['wan']; + $lancfg = $config['interfaces']['lan']; + $pptpdcfg = $config['pptpd']; + + $lanif = $lancfg['if']; + $wanif = get_real_wan_interface(); + + $lanip = $lancfg['ipaddr']; + $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']); + $lansn = $lancfg['subnet']; + + /* optional interfaces */ + $optcfg = array(); + + for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) { + $oc = $config['interfaces']['opt' . $i]; + + if (isset($oc['enable']) && $oc['if']) { + $oic = array(); + $oic['ip'] = $oc['ipaddr']; + $oic['if'] = $oc['if']; + $oic['sa'] = gen_subnet($oc['ipaddr'], $oc['subnet']); + $oic['sn'] = $oc['subnet']; + + $optcfg['opt' . $i] = $oic; + } + } + + if ($pptpdcfg['mode'] == "server") { + $pptpip = $pptpdcfg['localip']; + $pptpsa = $pptpdcfg['remoteip']; + $pptpsn = $g['pptp_subnet']; + } + + $rulei = 50000; + + /* add a rule to pass all traffic from/to the firewall, + so the user cannot lock himself out of the webGUI */ + $shaperrules = "add $rulei set 4 pass all from $lanip to any\n"; $rulei++; + $shaperrules .= "add $rulei set 4 pass all from any to $lanip\n"; $rulei++; + + /* generate rules */ + if (isset($config['pfqueueing']['rule'])) + foreach ($config['pfqueueing']['rule'] as $rule) { + + /* don't include disabled rules */ + if (isset($rule['disabled'])) { + $i++; + continue; + } + + /* does the rule deal with a PPTP interface? */ + if ($rule['interface'] == "pptp") { + + if ($pptpdcfg['mode'] != "server") { + $i++; + continue; + } + + $nif = $g['n_pptp_units']; + $ispptp = true; + } else { + + if (strstr($rule['interface'], "opt")) { + if (!array_key_exists($rule['interface'], $optcfg)) { + $i++; + continue; + } + } + + $nif = 1; + $ispptp = false; + } + + if ($pptpdcfg['mode'] != "server") { + if (($rule['source']['network'] == "pptp") || + ($rule['destination']['network'] == "pptp")) { + $i++; + continue; + } + } + + if (strstr($rule['source']['network'], "opt")) { + if (!array_key_exists($rule['source']['network'], $optcfg)) { + $i++; + continue; + } + } + if (strstr($rule['destination']['network'], "opt")) { + if (!array_key_exists($rule['destination']['network'], $optcfg)) { + $i++; + continue; + } + } + + /* check for unresolvable aliases */ + if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) { + $i++; + continue; + } + if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) { + $i++; + continue; + } + + for ($iif = 0; $iif < $nif; $iif++) { + + /* pipe or queue? */ + if (isset($rule['targetpipe']) && isset($config['pfqueueing']['pipe'][$rule['targetpipe']])) { + $pipen = $rule['targetpipe'] + 1; + $line = "add $rulei set 4 pipe $pipen "; $rulei++; + } else if (isset($rule['targetqueue']) && isset($config['pfqueueing']['queue'][$rule['targetqueue']])) { + $queuen = $rule['targetqueue'] + 1; + $line = "add $rulei set 4 queue $queuen "; $rulei++; + } else { + printf("Neither existing pipe nor queue found in rule $i\n"); + break; + } + + if (isset($rule['protocol'])) { + $line .= "{$rule['protocol']} "; + } else { + $line .= "all "; + } + + /* source address */ + if (isset($rule['source']['any'])) { + $src = "any"; + } else if ($rule['source']['network']) { + + if (strstr($rule['source']['network'], "opt")) { + $src = $optcfg[$rule['source']['network']]['sa'] . "/" . + $optcfg[$rule['source']['network']]['sn']; + } else { + switch ($rule['source']['network']) { + case 'lan': + $src = "$lansa/$lansn"; + break; + case 'pptp': + $src = "$pptpsa/$pptpsn"; + break; + } + } + } else if ($rule['source']['address']) { + $src = alias_expand($rule['source']['address']); + } + + if (!$src) { + printf("No source address found in rule $i\n"); + break; + } + + if (isset($rule['source']['not'])) { + $line .= "from not $src "; + } else { + $line .= "from $src "; + } + + if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) { + + if ($rule['source']['port']) { + $srcport = explode("-", $rule['source']['port']); + + if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) { + $line .= "{$srcport[0]} "; + } else { + $line .= "{$srcport[0]}-{$srcport[1]} "; + } + } + } + + /* destination address */ + if (isset($rule['destination']['any'])) { + $dst = "any"; + } else if ($rule['destination']['network']) { + + if (strstr($rule['destination']['network'], "opt")) { + $dst = $optcfg[$rule['destination']['network']]['sa'] . "/" . + $optcfg[$rule['destination']['network']]['sn']; + } else { + switch ($rule['destination']['network']) { + case 'lan': + $dst = "$lansa/$lansn"; + break; + case 'pptp': + $dst = "$pptpsa/$pptpsn"; + break; + } + } + } else if ($rule['destination']['address']) { + $dst = alias_expand($rule['destination']['address']); + } + + if (!$dst) { + printf("No destination address found in rule $i\n"); + break; + } + + if (isset($rule['destination']['not'])) { + $line .= "to not $dst "; + } else { + $line .= "to $dst "; + } + + if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) { + + if ($rule['destination']['port']) { + $dstport = explode("-", $rule['destination']['port']); + + if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) { + $line .= "{$dstport[0]} "; + } else { + $line .= "{$dstport[0]}-{$dstport[1]} "; + } + } + } + + if ($rule['iplen']) + $line .= "iplen {$rule['iplen']} "; + + if ($rule['iptos']) + $line .= "iptos {$rule['iptos']} "; + + if ($rule['tcpflags']) + $line .= "tcpflags {$rule['tcpflags']} "; + + if ($rule['direction'] == "in") + $line .= "in "; + else if ($rule['direction'] == "out") + $line .= "out "; + + if ($ispptp) { + $line .= "via ng" . ($iif+1); + } else { + if ($rule['interface'] == "wan") + $if = $wanif; + else + $if = $config['interfaces'][$rule['interface']]['if']; + + $line .= "via {$if}"; + } + + $line .= "\n"; + $shaperrules .= $line; + } + + $i++; + } + + /* generate pipes */ + if (isset($config['pfqueueing']['pipe'])) { + $pipei = 1; + foreach ($config['pfqueueing']['pipe'] as $pipe) { + $line = "pipe $pipei config bw {$pipe['bandwidth']}Kbit/s "; + + if ($pipe['delay']) { + $line .= "delay {$pipe['delay']} "; + } + + switch ($pipe['mask']) { + case 'source': + $line .= "mask src-ip 0xffffffff "; + break; + case 'destination': + $line .= "mask dst-ip 0xffffffff "; + break; + } + + $line .= "\n"; + $shaperrules .= $line; + $pipei++; + } + } + + /* generate queues */ + if (isset($config['pfqueueing']['queue'])) { + $queuei = 1; + foreach ($config['pfqueueing']['queue'] as $queue) { + + $pipen = $queue['targetpipe'] + 1; + if (!isset($queue['targetpipe']) || !isset($config['pfqueueing']['pipe'][$queue['targetpipe']])) { + printf("Pipe $pipen for queue $queuei not found!\n"); + continue; + } + + $line = "queue $queuei config pipe {$pipen}"; + $line .= " weight {$queue['weight']}"; + + switch ($queue['mask']) { + case 'source': + $line .= " mask src-ip 0xffffffff "; + break; + case 'destination': + $line .= " mask dst-ip 0xffffffff "; + break; + } + + $line .= "\n"; + $shaperrules .= $line; + $queuei++; + } + } + + return $shaperrules; +} + +?> diff --git a/etc/inc/system.inc b/etc/inc/system.inc new file mode 100644 index 0000000..d2c0b33 --- /dev/null +++ b/etc/inc/system.inc @@ -0,0 +1,563 @@ +<?php +/* + system.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); + +function system_resolvconf_generate($dynupdate = false) { + global $config, $g; + + $syscfg = $config['system']; + + $fd = fopen("{$g['varetc_path']}/resolv.conf", "w"); + if (!$fd) { + printf("Error: cannot open resolv.conf in system_resolvconf_generate().\n"); + return 1; + } + + $resolvconf = "domain {$syscfg['domain']}\n"; + + $havedns = false; + + if (isset($syscfg['dnsallowoverride'])) { + /* get dynamically assigned DNS servers (if any) */ + $nfd = @fopen("{$g['varetc_path']}/nameservers.conf", "r"); + if ($nfd) { + while (!feof($nfd)) { + $dnss = trim(fgets($nfd)); + if ($dnss) { + $resolvconf .= "nameserver $dnss\n"; + $havedns = true; + } + } + fclose($nfd); + } + } + if (!$havedns && is_array($syscfg['dnsserver'])) { + foreach ($syscfg['dnsserver'] as $ns) { + if ($ns) + $resolvconf .= "nameserver $ns\n"; + $havedns = true; + } + } + + fwrite($fd, $resolvconf); + fclose($fd); + + if (!$g['booting']) { + /* restart dhcpd (nameservers may have changed) */ + if (!$dynupdate) + services_dhcpd_configure(); + } + + return 0; +} + +function system_hosts_generate() { + global $config, $g; + + $syscfg = $config['system']; + $lancfg = $config['interfaces']['lan']; + $dnsmasqcfg = $config['dnsmasq']; + + if (!is_array($dnsmasqcfg['hosts'])) { + $dnsmasqcfg['hosts'] = array(); + } + $hostscfg = $dnsmasqcfg['hosts']; + + $fd = fopen("{$g['varetc_path']}/hosts", "w"); + if (!$fd) { + printf("Error: cannot open hosts file in system_hosts_generate().\n"); + return 1; + } + + $hosts = <<<EOD +127.0.0.1 localhost localhost.{$syscfg['domain']} +{$lancfg['ipaddr']} {$syscfg['hostname']}.{$syscfg['domain']} {$syscfg['hostname']} + +EOD; + + foreach ($hostscfg as $host) { + if ($host['host']) + $hosts .= "{$host['ip']} {$host['host']}.{$host['domain']} {$host['host']}\n"; + else + $hosts .= "{$host['ip']} {$host['domain']}\n"; + } + fwrite($fd, $hosts); + fclose($fd); + + return 0; +} + +function system_hostname_configure() { + global $config, $g; + + $syscfg = $config['system']; + + /* set hostname */ + return mwexec("/bin/hostname " . + escapeshellarg("{$syscfg['hostname']}.{$syscfg['domain']}")); +} + +function system_routing_configure() { + global $config, $g; + + /* clear out old routes, if necessary */ + if (file_exists("{$g['vardb_path']}/routes.db")) { + $fd = fopen("{$g['vardb_path']}/routes.db", "r"); + if (!$fd) { + printf("Error: cannot open routes DB file in system_routing_configure().\n"); + return 1; + } + while (!feof($fd)) { + $oldrt = fgets($fd); + if ($oldrt) + mwexec("/sbin/route delete " . escapeshellarg($oldrt)); + } + fclose($fd); + unlink("{$g['vardb_path']}/routes.db"); + } + + if (is_array($config['staticroutes']['route'])) { + + $fd = fopen("{$g['vardb_path']}/routes.db", "w"); + if (!$fd) { + printf("Error: cannot open routes DB file in system_routing_configure().\n"); + return 1; + } + + foreach ($config['staticroutes']['route'] as $rtent) { + mwexec("/sbin/route add " . escapeshellarg($rtent['network']) . + " " . escapeshellarg($rtent['gateway'])); + + /* record route so it can be easily removed later (if necessary) */ + fwrite($fd, $rtent['network'] . "\n"); + } + + fclose($fd); + } + + return 0; +} + +function system_routing_enable() { + global $config, $g; + + return mwexec("/sbin/sysctl net.inet.ip.forwarding=1"); +} + +function system_syslogd_start() { + global $config, $g; + + $syslogcfg = $config['syslog']; + + if ($g['booting']) + echo "Starting syslog service... "; + else + killbypid("{$g['varrun_path']}/syslog.pid"); + + if (isset($syslogcfg['enable'])) { + + /* write syslog.conf */ + $fd = fopen("{$g['varetc_path']}/syslog.conf", "w"); + if (!$fd) { + printf("Error: cannot open syslog.conf in system_syslogd_start().\n"); + return 1; + } + + $syslogconf = <<<EOD +local0.* %/var/log/filter.log +local3.* %/var/log/vpn.log +local7.* %/var/log/dhcpd.log +*.notice;kern.debug;lpr.info;mail.crit;news.err;local0.none;local3.none;local7.none %/var/log/system.log +security.* %/var/log/system.log +auth.info;authpriv.info;daemon.info %/var/log/system.log +*.emerg * + +EOD; + + if (isset($syslogcfg['filter'])) { + $syslogconf .= <<<EOD +local0.* @{$syslogcfg['remoteserver']} + +EOD; + } + + if (isset($syslogcfg['vpn'])) { + $syslogconf .= <<<EOD +local3.* @{$syslogcfg['remoteserver']} + +EOD; + } + + if (isset($syslogcfg['dhcp'])) { + $syslogconf .= <<<EOD +local7.* @{$syslogcfg['remoteserver']} + +EOD; + } + + if (isset($syslogcfg['system'])) { + $syslogconf .= <<<EOD +*.notice;kern.debug;lpr.info;mail.crit;news.err;local0.none;local7.none @{$syslogcfg['remoteserver']} +security.* @{$syslogcfg['remoteserver']} +auth.info;authpriv.info;daemon.info @{$syslogcfg['remoteserver']} +*.emerg @{$syslogcfg['remoteserver']} + +EOD; + } + + fwrite($fd, $syslogconf); + fclose($fd); + + $retval = mwexec("/usr/sbin/syslogd -s -f {$g['varetc_path']}/syslog.conf"); + + } else { + $retval = mwexec("/usr/sbin/syslogd -ss"); + } + + if ($g['booting']) + echo "done\n"; + + return $retval; +} + +function system_pccard_start() { + global $config, $g; + + if ($g['booting']) + echo "Initializing PC cards... "; + + /* kill any running pccardd */ + killbypid("{$g['varrun_path']}/pccardd.pid"); + + /* fire up pccardd */ + $res = mwexec("/usr/sbin/pccardd -z -f {$g['etc_path']}/pccard.conf"); + + if ($g['booting']) { + if ($res == 0) + echo "done\n"; + else + echo "failed (probably no PC card controller present)\n"; + } + + return $res; +} + +function system_webgui_start() { + global $config, $g; + + if ($g['booting']) + echo "Starting webGUI... "; + + /* kill any running mini_httpd */ + killbypid("{$g['varrun_path']}/mini_httpd.pid"); + + /* generate password file */ + system_password_configure(); + + chdir($g['www_path']); + + /* non-standard port? */ + if ($config['system']['webgui']['port']) + $portarg = "-p {$config['system']['webgui']['port']}"; + else + $portarg = ""; + + if ($config['system']['webgui']['protocol'] == "https") { + + if ($config['system']['webgui']['certificate'] && $config['system']['webgui']['private-key']) { + $cert = base64_decode($config['system']['webgui']['certificate']); + $key = base64_decode($config['system']['webgui']['private-key']); + } else { + /* default certificate/key */ + $cert = <<<EOD +-----BEGIN CERTIFICATE----- +MIIBlDCB/gIBADANBgkqhkiG9w0BAQQFADATMREwDwYDVQQKEwhtMG4wd2FsbDAe +Fw0wMzA5MDgxNzAzNDZaFw0wNDA5MDcxNzAzNDZaMBMxETAPBgNVBAoTCG0wbjB3 +YWxsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAShszhFz+o8lsMWTGgTxs +TMPR+v4+qL5jXDyY97MLTGFK7aqQOtpIQc+TcTc4jklgOVlHoR7oBXrsi8YrbCd+ +83LPQmQoSPC0VqhfU3uYf3NzxiK8r97aPCsmWgwT2pQ6TcESTm6sF7nLprOf/zFP +C4jE2fvjkbzyVolPywBuewIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAK2D8NqQSlUs +pFCe5J9ue1LrjfGHHy4HE9zA9avgrz3Qju+1JOshEwy/1BJjZ93tQUbiRS7RwvDO +4crGG4IejjhFczzA2CIX3rd2rYM2oGpojKgm5YuuhV5lYPwAHUOLbBaLOVqlLhzw +VqjD7R2DkXUIfhJ5ZekqK5ZwzqJXta8U +-----END CERTIFICATE----- + +EOD; + + $key = <<<EOD +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDAShszhFz+o8lsMWTGgTxsTMPR+v4+qL5jXDyY97MLTGFK7aqQ +OtpIQc+TcTc4jklgOVlHoR7oBXrsi8YrbCd+83LPQmQoSPC0VqhfU3uYf3NzxiK8 +r97aPCsmWgwT2pQ6TcESTm6sF7nLprOf/zFPC4jE2fvjkbzyVolPywBuewIDAQAB +AoGAbJJrQW9fQrggJuLMz/hwsYW2m31oyOBmf5u463YQtjRuSuxe/gj87weZuNqY +H2rXq2k2K+ehl8hgW+egASyUL3L7kCkEAsVREujKTEyhSqqIRDPWTxo9S/YA9Gvn +2ZnJvkrcKjqCO9aHX3rvJOK/ErYI6akctgI3KmgkYw5XNmECQQDuZU97RTWH9rmP +aQr57ysNXxgFsyhetOOqeYkPtIVwpOiNbfwE1zi5RGdtO4Ku3fG1lV4J2UoWJ9yD +awdoyYIHAkEAzn0xJ90IjPsHk+8SODEj5JGdHSZPNu1tgtrbjEi9sfGWg4K7XTxr +QW90pWb1bKKU1uh5FzW6OhnFfuQXt1kC7QJAPSthqY+onKqCEnoxhtAHi/bKgyvl +P+fKQwPMV2tKkgy+XwvJjrRqqZ8TqsOKVLQ+QQmCh6RpjiXMPyxHSmvqIQJBAKLR +HF1ucDuaBROkwx0DwmWMW/KMLpIFDQDNSaiIAuu4rxHrl4mhBoGGPNffI04RtILw +s+qVNs5xW8T+XaT4ztECQQDFHPnZeoPWE5z+AX/UUQIUWaDExz3XRzmIxRbOrlFi +CsF1s0TdJLi/wzNQRAL37A8vqCeVFR/ng3Xpg96Yg+8Z +-----END RSA PRIVATE KEY----- + +EOD; + } + + $fd = fopen("{$g['varetc_path']}/cert.pem", "w"); + if (!$fd) { + printf("Error: cannot open cert.pem in system_webgui_start().\n"); + return 1; + } + chmod("{$g['varetc_path']}/cert.pem", 0600); + fwrite($fd, $cert); + fwrite($fd, "\n"); + fwrite($fd, $key); + fclose($fd); + + $res = mwexec("/usr/local/sbin/mini_httpd -S -E {$g['varetc_path']}/cert.pem" . + " -c \"**.php|**.cgi\" -u root -maxproc 16 $portarg" . + " -i {$g['varrun_path']}/mini_httpd.pid"); + } else { + $res = mwexec("/usr/local/sbin/mini_httpd -c \"**.php|**.cgi\" -u root" . + " -maxproc 16 $portarg -i {$g['varrun_path']}/mini_httpd.pid"); + } + + if ($g['booting']) { + if ($res == 0) + echo "done\n"; + else + echo "failed\n"; + } + + return $res; +} + +function system_password_configure() { + global $config, $g; + + $fd = fopen("{$g['varrun_path']}/htpasswd", "w"); + if (!$fd) { + printf("Error: cannot open htpasswd in system_password_configure().\n"); + return 1; + } + + if ($config['system']['username']) + $username = $config['system']['username']; + else + $username = "admin"; + + fwrite($fd, $username . ":" . $config['system']['password'] . "\n"); + fclose($fd); + chmod("{$g['varrun_path']}/htpasswd", 0600); + + return 0; +} + +function system_timezone_configure() { + global $config, $g; + + $syscfg = $config['system']; + + if ($g['booting']) + echo "Initializing timezone... "; + + /* extract appropriate timezone file */ + $timezone = $syscfg['timezone']; + if (!$timezone) + $timezone = "Etc/UTC"; + + exec("/usr/bin/tar xzfO /usr/share/zoneinfo.tgz " . + escapeshellarg($timezone) . " > /etc/localtime"); + + if ($g['booting']) + echo "done\n"; +} + +function system_ntp_configure() { + global $config, $g; + + $syscfg = $config['system']; + + if ($g['booting']) + echo "Starting NTP client... "; + else { + killbypid("{$g['varrun_path']}/runmsntp.pid"); + killbypid("{$g['varrun_path']}/msntp.pid"); + } + + /* start ntp client if needed - needs to be forced into background */ + $updateinterval = $syscfg['time-update-interval']; + + if ($updateinterval > 0) { + if ($updateinterval < 6) + $updateinterval = 6; + + $timeservers = ""; + foreach (explode(' ', $syscfg['timeservers']) as $ts) + $timeservers .= " " . $ts; + + mwexec_bg("/usr/local/bin/runmsntp.sh " . + escapeshellarg("{$g['varrun_path']}/runmsntp.pid") . " " . + escapeshellarg("{$g['varrun_path']}/msntp.pid") . " " . + escapeshellarg($updateinterval) . " " . + escapeshellarg($timeservers)); + } + + if ($g['booting']) + echo "done\n"; +} + +function system_reboot() { + global $g; + + system_reboot_cleanup(); + + mwexec("nohup /etc/rc.reboot > /dev/null 2>&1 &"); +} + +function system_reboot_sync() { + global $g; + + system_reboot_cleanup(); + + mwexec("/etc/rc.reboot > /dev/null 2>&1"); +} + +function system_reboot_cleanup() { + captiveportal_radius_stop_all(); +} + +function system_do_shell_commands($early = 0) { + global $config, $g; + + if ($early) + $cmdn = "earlyshellcmd"; + else + $cmdn = "shellcmd"; + + if (is_array($config['system'][$cmdn])) { + + foreach ($config['system'][$cmdn] as $cmd) { + exec($cmd); + } + } +} + +function system_do_extensions() { + global $config, $g; + + if (!is_dir("{$g['etc_path']}/inc/ext")) + return; + + $dh = @opendir("{$g['etc_path']}/inc/ext"); + if ($dh) { + while (($extd = readdir($dh)) !== false) { + if (($extd === ".") || ($extd === "..")) + continue; + $rcfile = "{$g['etc_path']}/inc/ext/" . $extd . "/rc"; + if (file_exists($rcfile)) + passthru($rcfile); + } + closedir($dh); + } +} + +function system_console_configure() { + global $config, $g; + + if (isset($config['system']['disableconsolemenu'])) { + touch("{$g['varetc_path']}/disableconsole"); + } else { + unlink_if_exists("{$g['varetc_path']}/disableconsole"); + } +} + +function system_dmesg_save() { + global $g; + + exec("/sbin/dmesg", $dmesg); + + /* find last copyright line (output from previous boots may be present) */ + $lastcpline = 0; + + for ($i = 0; $i < count($dmesg); $i++) { + if (strstr($dmesg[$i], "Copyright (c) 1992-")) + $lastcpline = $i; + } + + $fd = fopen("{$g['varlog_path']}/dmesg.boot", "w"); + if (!$fd) { + printf("Error: cannot open dmesg.boot in system_dmesg_save().\n"); + return 1; + } + + for ($i = $lastcpline; $i < count($dmesg); $i++) + fwrite($fd, $dmesg[$i] . "\n"); + + fclose($fd); + + return 0; +} + +function system_set_harddisk_standby() { + global $g, $config; + + if ($g['platform'] != "generic-pc") + return; + + if (isset($config['system']['harddiskstandby'])) { + if ($g['booting']) { + echo 'Setting harddisk standby time... '; + } + + $standby = $config['system']['harddiskstandby']; + // Check for a numeric value + if (is_numeric($standby)) { + // Sync the disk(s) + mwexec('/bin/sync'); + if (!mwexec('/sbin/sysctl hw.ata.standby=' . ((int)$standby))) { + // Reinitialize ATA-drives + mwexec('/usr/local/sbin/atareinit'); + if ($g['booting']) { + echo "done\n"; + } + } else if ($g['booting']) { + echo "failed\n"; + } + } else if ($g['booting']) { + echo "failed\n"; + } + } +} + +?> diff --git a/etc/inc/util.inc b/etc/inc/util.inc new file mode 100644 index 0000000..2b3fa67 --- /dev/null +++ b/etc/inc/util.inc @@ -0,0 +1,421 @@ +<?php +/* + util.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* kill a process by pid file */ +function killbypid($pidfile) { + sigkillbypid($pidfile, "TERM"); +} + +/* sigkill a process by pid file */ +function sigkillbypid($pidfile, $sig) { + if (file_exists($pidfile)) { + mwexec("/bin/kill -s $sig `/bin/cat " . $pidfile . "`"); + } +} + +/* kill a process by name */ +function killbyname($procname) { + mwexec("/usr/bin/killall " . escapeshellarg($procname)); +} + +/* return the subnet address given a host address and a subnet bit count */ +function gen_subnet($ipaddr, $bits) { + if (!is_ipaddr($ipaddr) || !is_numeric($bits)) + return ""; + + return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits)); +} + +/* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */ +function gen_subnet_max($ipaddr, $bits) { + if (!is_ipaddr($ipaddr) || !is_numeric($bits)) + return ""; + + return long2ip(ip2long($ipaddr) | ~gen_subnet_mask_long($bits)); +} + +/* returns a subnet mask (long given a bit count) */ +function gen_subnet_mask_long($bits) { + $sm = 0; + for ($i = 0; $i < $bits; $i++) { + $sm >>= 1; + $sm |= 0x80000000; + } + return $sm; +} + +/* same as above but returns a string */ +function gen_subnet_mask($bits) { + return long2ip(gen_subnet_mask_long($bits)); +} + +function is_numericint($arg) { + return (preg_match("/[^0-9]/", $arg) ? false : true); +} + +/* returns true if $ipaddr is a valid dotted IPv4 address */ +function is_ipaddr($ipaddr) { + if (!is_string($ipaddr)) + return false; + + $ip_long = ip2long($ipaddr); + $ip_reverse = long2ip($ip_long); + + if ($ipaddr == $ip_reverse) + return true; + else + return false; +} + +/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */ +function is_ipaddroralias($ipaddr) { + + global $aliastable; + + if (isset($aliastable[$ipaddr]) && is_ipaddr($aliastable[$ipaddr])) + return true; + else + return is_ipaddr($ipaddr); +} + +/* returns true if $ipaddr is a valid dotted IPv4 address or any alias */ +function is_ipaddroranyalias($ipaddr) { + + global $aliastable; + + if (isset($aliastable[$ipaddr])) + return true; + else + return is_ipaddr($ipaddr); +} + +/* returns true if $subnet is a valid subnet in CIDR format */ +function is_subnet($subnet) { + if (!is_string($subnet)) + return false; + + list($hp,$np) = explode('/', $subnet); + + if (!is_ipaddr($hp)) + return false; + + if (!is_numeric($np) || ($np < 1) || ($np > 32)) + return false; + + return true; +} + +/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */ +function is_subnetoralias($subnet) { + + global $aliastable; + + if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet])) + return true; + else + return is_subnet($subnet); +} + +/* returns true if $hostname is a valid hostname */ +function is_hostname($hostname) { + if (!is_string($hostname)) + return false; + + if (preg_match("/^[a-z0-9\-]+$/i", $hostname)) + return true; + else + return false; +} + +/* returns true if $domain is a valid domain name */ +function is_domain($domain) { + if (!is_string($domain)) + return false; + + if (preg_match("/^([a-z0-9\-]+\.?)*$/i", $domain)) + return true; + else + return false; +} + +/* returns true if $uname is a valid DynDNS username */ +function is_dyndns_username($uname) { + if (!is_string($uname)) + return false; + + if (preg_match("/[^a-z0-9\-.@_]/i", $uname)) + return false; + else + return true; +} + +/* returns true if $macaddr is a valid MAC address */ +function is_macaddr($macaddr) { + if (!is_string($macaddr)) + return false; + + $maca = explode(":", $macaddr); + if (count($maca) != 6) + return false; + + foreach ($maca as $macel) { + if (($macel === "") || (strlen($macel) > 2)) + return false; + if (preg_match("/[^0-9a-f]/i", $macel)) + return false; + } + + return true; +} + +/* returns true if $name is a valid name for an alias */ +function is_validaliasname($name) { + if (!preg_match("/[^a-zA-Z0-9]/", $name)) + return true; + else + return false; +} + +/* returns true if $port is a valid TCP/UDP port */ +function is_port($port) { + if (!is_numericint($port)) + return false; + + if (($port < 1) || ($port > 65535)) + return false; + else + return true; +} + +/* returns a list of interfaces with MAC addresses + (skips VLAN and other virtual interfaces) */ +function get_interface_list() { + + global $g; + + /* build interface list with netstat */ + exec("/usr/bin/netstat -inW -f link", $linkinfo); + array_shift($linkinfo); + + $iflist = array(); + + foreach ($linkinfo as $link) { + $alink = preg_split("/\s+/", $link); + $ifname = chop($alink[0]); + + if (substr($ifname, -1) == "*") + $ifname = substr($ifname, 0, strlen($ifname) - 1); + + if (!preg_match("/^(ppp|sl|gif|faith|lo|ng|vlan)/", $ifname)) { + $iflist[$ifname] = array(); + + $iflist[$ifname]['mac'] = chop($alink[3]); + $iflist[$ifname]['up'] = false; + + /* find out if the link on this interface is up */ + unset($ifinfo); + exec("/sbin/ifconfig {$ifname}", $ifinfo); + + foreach ($ifinfo as $ifil) { + if (preg_match("/status: (.*)$/", $ifil, $matches)) { + if ($matches[1] == "active") + $iflist[$ifname]['up'] = true; + break; + } + } + } + } + + return $iflist; +} + +/* wrapper for exec() */ +function mwexec($command) { + + global $g; + + if ($g['debug']) { + if (!$_SERVER['REMOTE_ADDR']) + echo "mwexec(): $command\n"; + passthru($command, $retval); + } else { + exec("$command > /dev/null 2>&1", $oarr, $retval); + } + + return $retval; +} + +/* wrapper for exec() in background */ +function mwexec_bg($command) { + + global $g; + + if ($g['debug']) { + if (!$_SERVER['REMOTE_ADDR']) + echo "mwexec(): $command\n"; + } + + exec("nohup $command > /dev/null 2>&1 &"); +} + +/* unlink a file, if it exists */ +function unlink_if_exists($fn) { + if (file_exists($fn)) + unlink($fn); +} + +/* make a global alias table (for faster lookups) */ +function alias_make_table() { + + global $config, $g, $aliastable; + + $aliastable = array(); + + if (is_array($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $alias) { + if ($alias['name']) + $aliastable[$alias['name']] = $alias['address']; + } + } +} + +/* check if an alias exists */ +function is_alias($name) { + + global $aliastable; + + return isset($aliastable[$name]); +} + +/* expand a host or network alias, if necessary */ +function alias_expand($name) { + + global $aliastable; + + if (isset($aliastable[$name])) + return $aliastable[$name]; + else if (is_ipaddr($name) || is_subnet($name)) + return $name; + else + return null; +} + +/* expand a host alias, if necessary */ +function alias_expand_host($name) { + + global $aliastable; + + if (isset($aliastable[$name]) && is_ipaddr($aliastable[$name])) + return $aliastable[$name]; + else if (is_ipaddr($name)) + return $name; + else + return null; +} + +/* expand a network alias, if necessary */ +function alias_expand_net($name) { + + global $aliastable; + + if (isset($aliastable[$name]) && is_subnet($aliastable[$name])) + return $aliastable[$name]; + else if (is_subnet($name)) + return $name; + else + return null; +} + +/* find out whether two subnets overlap */ +function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) { + + if (!is_numeric($bits1)) + $bits1 = 32; + if (!is_numeric($bits2)) + $bits2 = 32; + + if ($bits1 < $bits2) + $relbits = $bits1; + else + $relbits = $bits2; + + $sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1); + $sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2); + + if ($sn1 == $sn2) + return true; + else + return false; +} + +/* compare two IP addresses */ +function ipcmp($a, $b) { + if (ip2long($a) < ip2long($b)) + return -1; + else if (ip2long($a) > ip2long($b)) + return 1; + else + return 0; +} + +/* return true if $addr is in $subnet, false if not */ +function ip_in_subnet($addr,$subnet) { + list($ip, $mask) = explode('/', $subnet); + $mask = 0xffffffff << (32 - $mask); + return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask)); +} + +/* verify (and remove) the digital signature on a file - returns 0 if OK */ +function verify_digital_signature($fname) { + + global $g; + + return mwexec("/usr/local/bin/verifysig " . + escapeshellarg("{$g['etc_path']}/pubkey.pem") . " " . + escapeshellarg($fname)); +} + +/* obtain MAC address given an IP address by looking at the ARP table */ +function arp_get_mac_by_ip($ip) { + exec("/usr/sbin/arp -n {$ip}", $arpoutput); + + if ($arpoutput[0]) { + $arpi = explode(" ", $arpoutput[0]); + $macaddr = $arpi[3]; + if (is_macaddr($macaddr)) + return $macaddr; + else + return false; + } + + return false; +} + +?> diff --git a/etc/inc/vpn.inc b/etc/inc/vpn.inc new file mode 100644 index 0000000..b73af46 --- /dev/null +++ b/etc/inc/vpn.inc @@ -0,0 +1,559 @@ +<?php +/* + vpn.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* include all configuration functions */ +require_once("functions.inc"); + +function vpn_ipsec_configure($ipchg = false) { + global $config, $g; + + $curwanip = get_current_wan_address(); + + $syscfg = $config['system']; + $ipseccfg = $config['ipsec']; + $lancfg = $config['interfaces']['lan']; + $lanip = $lancfg['ipaddr']; + $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']); + $lansn = $lancfg['subnet']; + + if ($g['booting']) { + if (!isset($ipseccfg['enable'])) + return 0; + + echo "Configuring IPsec VPN... "; + } else { + /* kill racoon */ + killbypid("{$g['varrun_path']}/racoon.pid"); + + /* wait for process to die */ + sleep(2); + + /* send a SIGKILL to be sure */ + sigkillbypid("{$g['varrun_path']}/racoon.pid", "KILL"); + } + + /* flush SPD and SAD */ + mwexec("/usr/sbin/setkey -FP"); + mwexec("/usr/sbin/setkey -F"); + + /* prefer old SAs only for 30 seconds, then use the new one */ + mwexec("/sbin/sysctl -w net.key.preferred_oldsa=-30"); + + if (isset($ipseccfg['enable'])) { + + if (!$curwanip) { + /* IP address not configured yet, exit */ + if ($g['booting']) + echo "done\n"; + return 0; + } + + if ((is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) || + isset($ipseccfg['mobileclients']['enable'])) { + + if (is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) { + + /* generate spd.conf */ + $fd = fopen("{$g['varetc_path']}/spd.conf", "w"); + if (!$fd) { + printf("Error: cannot open spd.conf in vpn_ipsec_configure().\n"); + return 1; + } + + $spdconf = ""; + + $spdconf .= "spdadd {$lansa}/{$lansn} {$lanip}/32 any -P in none;\n"; + $spdconf .= "spdadd {$lanip}/32 {$lansa}/{$lansn} any -P out none;\n"; + + foreach ($ipseccfg['tunnel'] as $tunnel) { + + if (isset($tunnel['disabled'])) + continue; + + $ep = vpn_endpoint_determine($tunnel, $curwanip); + if (!$ep) + continue; + + vpn_localnet_determine($tunnel['local-subnet'], $sa, $sn); + + $spdconf .= "spdadd {$sa}/{$sn} " . + "{$tunnel['remote-subnet']} any -P out ipsec " . + "{$tunnel['p2']['protocol']}/tunnel/{$ep}-" . + "{$tunnel['remote-gateway']}/unique;\n"; + + $spdconf .= "spdadd {$tunnel['remote-subnet']} " . + "{$sa}/{$sn} any -P in ipsec " . + "{$tunnel['p2']['protocol']}/tunnel/{$tunnel['remote-gateway']}-" . + "{$ep}/unique;\n"; + } + + fwrite($fd, $spdconf); + fclose($fd); + + /* load SPD */ + mwexec("/usr/sbin/setkey -c < {$g['varetc_path']}/spd.conf"); + } + + /* generate racoon.conf */ + $fd = fopen("{$g['varetc_path']}/racoon.conf", "w"); + if (!$fd) { + printf("Error: cannot open racoon.conf in vpn_ipsec_configure().\n"); + return 1; + } + + $racoonconf = "path pre_shared_key \"{$g['varetc_path']}/psk.txt\";\n\n"; + + if (is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) + foreach ($ipseccfg['tunnel'] as $tunnel) { + + if (isset($tunnel['disabled'])) + continue; + + $ep = vpn_endpoint_determine($tunnel, $curwanip); + if (!$ep) + continue; + + vpn_localnet_determine($tunnel['local-subnet'], $sa, $sn); + + if (isset($tunnel['p1']['myident']['myaddress'])) { + $myidentt = "address"; + $myident = $ep; + } else if (isset($tunnel['p1']['myident']['address'])) { + $myidentt = "address"; + $myident = $tunnel['p1']['myident']['address']; + } else if (isset($tunnel['p1']['myident']['fqdn'])) { + $myidentt = "fqdn"; + $myident = $tunnel['p1']['myident']['fqdn']; + } else if (isset($tunnel['p1']['myident']['ufqdn'])) { + $myidentt = "user_fqdn"; + $myident = $tunnel['p1']['myident']['ufqdn']; + } + + $racoonconf .= <<<EOD +remote {$tunnel['remote-gateway']} \{ + exchange_mode {$tunnel['p1']['mode']}; + my_identifier {$myidentt} "{$myident}"; + peers_identifier address {$tunnel['remote-gateway']}; + initial_contact on; + support_proxy on; + proposal_check obey; + + proposal \{ + encryption_algorithm {$tunnel['p1']['encryption-algorithm']}; + hash_algorithm {$tunnel['p1']['hash-algorithm']}; + authentication_method pre_shared_key; + dh_group {$tunnel['p1']['dhgroup']}; + +EOD; + if ($tunnel['p1']['lifetime']) + $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n"; + + $racoonconf .= " }\n"; + + if ($tunnel['p1']['lifetime']) + $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n"; + + $racoonconf .= "}\n\n"; + + $p2ealgos = join(",", $tunnel['p2']['encryption-algorithm-option']); + $p2halgos = join(",", $tunnel['p2']['hash-algorithm-option']); + + $racoonconf .= <<<EOD +sainfo address {$sa}/{$sn} any address {$tunnel['remote-subnet']} any \{ + encryption_algorithm {$p2ealgos}; + authentication_algorithm {$p2halgos}; + compression_algorithm deflate; + +EOD; + + if ($tunnel['p2']['pfsgroup']) + $racoonconf .= " pfs_group {$tunnel['p2']['pfsgroup']};\n"; + + if ($tunnel['p2']['lifetime']) + $racoonconf .= " lifetime time {$tunnel['p2']['lifetime']} secs;\n"; + + $racoonconf .= "}\n\n"; + } + + /* mobile clients? */ + if (isset($ipseccfg['mobileclients']['enable'])) { + + $tunnel = $ipseccfg['mobileclients']; + + if (isset($tunnel['p1']['myident']['myaddress'])) { + $myidentt = "address"; + $myident = $curwanip; + } else if (isset($tunnel['p1']['myident']['address'])) { + $myidentt = "address"; + $myident = $tunnel['p1']['myident']['address']; + } else if (isset($tunnel['p1']['myident']['fqdn'])) { + $myidentt = "fqdn"; + $myident = $tunnel['p1']['myident']['fqdn']; + } else if (isset($tunnel['p1']['myident']['ufqdn'])) { + $myidentt = "user_fqdn"; + $myident = $tunnel['p1']['myident']['ufqdn']; + } + + $racoonconf .= <<<EOD +remote anonymous \{ + exchange_mode {$tunnel['p1']['mode']}; + my_identifier {$myidentt} "{$myident}"; + initial_contact on; + passive on; + generate_policy on; + support_proxy on; + proposal_check obey; + + proposal \{ + encryption_algorithm {$tunnel['p1']['encryption-algorithm']}; + hash_algorithm {$tunnel['p1']['hash-algorithm']}; + authentication_method pre_shared_key; + dh_group {$tunnel['p1']['dhgroup']}; + +EOD; + if ($tunnel['p1']['lifetime']) + $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n"; + + $racoonconf .= " }\n"; + + if ($tunnel['p1']['lifetime']) + $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n"; + + $racoonconf .= "}\n\n"; + + $p2ealgos = join(",", $tunnel['p2']['encryption-algorithm-option']); + $p2halgos = join(",", $tunnel['p2']['hash-algorithm-option']); + + $racoonconf .= <<<EOD +sainfo anonymous \{ + encryption_algorithm {$p2ealgos}; + authentication_algorithm {$p2halgos}; + compression_algorithm deflate; + +EOD; + + if ($tunnel['p2']['pfsgroup']) + $racoonconf .= " pfs_group {$tunnel['p2']['pfsgroup']};\n"; + + if ($tunnel['p2']['lifetime']) + $racoonconf .= " lifetime time {$tunnel['p2']['lifetime']} secs;\n"; + + $racoonconf .= "}\n\n"; + } + + fwrite($fd, $racoonconf); + fclose($fd); + + /* generate psk.txt */ + $fd = fopen("{$g['varetc_path']}/psk.txt", "w"); + if (!$fd) { + printf("Error: cannot open psk.txt in vpn_ipsec_configure().\n"); + return 1; + } + + $pskconf = ""; + + if (is_array($ipseccfg['tunnel'])) { + foreach ($ipseccfg['tunnel'] as $tunnel) { + if (isset($tunnel['disabled'])) + continue; + $pskconf .= "{$tunnel['remote-gateway']} {$tunnel['p1']['pre-shared-key']}\n"; + } + } + + /* add PSKs for mobile clients */ + if (is_array($ipseccfg['mobilekey'])) { + foreach ($ipseccfg['mobilekey'] as $key) { + $pskconf .= "{$key['ident']} {$key['pre-shared-key']}\n"; + } + } + + fwrite($fd, $pskconf); + fclose($fd); + chmod("{$g['varetc_path']}/psk.txt", 0600); + + /* start racoon */ + mwexec("/usr/local/sbin/racoon -d -f {$g['varetc_path']}/racoon.conf"); + + foreach ($ipseccfg['tunnel'] as $tunnel) { + if (isset($tunnel['auto'])) { + $remotehost = substr($tunnel['remote-subnet'],0,strpos($tunnel['remote-subnet'],"/")); + $srchost = vpn_endpoint_determine($tunnel, $curwanip); + if ($srchost) + mwexec_bg("/sbin/ping -c 1 -S {$srchost} {$remotehost}"); + } + } + } + } + + if (!$g['booting']) { + /* reload the filter */ + filter_configure(); + } + + if ($g['booting']) + echo "done\n"; + + return 0; +} + +function vpn_pptpd_configure() { + global $config, $g; + + $syscfg = $config['system']; + $pptpdcfg = $config['pptpd']; + + if ($g['booting']) { + if (!$pptpdcfg['mode'] || ($pptpdcfg['mode'] == "off")) + return 0; + + echo "Configuring PPTP VPN service... "; + } else { + /* kill mpd */ + killbypid("{$g['varrun_path']}/mpd-vpn.pid"); + + /* wait for process to die */ + sleep(2); + + /* remove mpd.conf, if it exists */ + unlink_if_exists("{$g['varetc_path']}/mpd-vpn/mpd.conf"); + unlink_if_exists("{$g['varetc_path']}/mpd-vpn/mpd.links"); + unlink_if_exists("{$g['varetc_path']}/mpd-vpn/mpd.secret"); + } + + /* make sure mpd-vpn directory exists */ + if (!file_exists("{$g['varetc_path']}/mpd-vpn")) + mkdir("{$g['varetc_path']}/mpd-vpn"); + + switch ($pptpdcfg['mode']) { + + case 'server': + + /* write mpd.conf */ + $fd = fopen("{$g['varetc_path']}/mpd-vpn/mpd.conf", "w"); + if (!$fd) { + printf("Error: cannot open mpd.conf in vpn_pptpd_configure().\n"); + return 1; + } + + $mpdconf = <<<EOD +pptpd: + +EOD; + + for ($i = 0; $i < $g['n_pptp_units']; $i++) { + $mpdconf .= " load pt{$i}\n"; + } + + for ($i = 0; $i < $g['n_pptp_units']; $i++) { + + $clientip = long2ip(ip2long($pptpdcfg['remoteip']) + $i); + $ngif = "ng" . ($i+1); + + $mpdconf .= <<<EOD + +pt{$i}: + new -i {$ngif} pt{$i} pt{$i} + set ipcp ranges {$pptpdcfg['localip']}/32 {$clientip}/32 + load pts + +EOD; + } + + $mpdconf .= <<<EOD + +pts: + set iface disable on-demand + set iface enable proxy-arp + set iface enable tcpmssfix + set iface idle 1800 + set iface up-script /usr/local/sbin/vpn-linkup + set iface down-script /usr/local/sbin/vpn-linkdown + set bundle enable multilink + set bundle enable crypt-reqd + set link yes acfcomp protocomp + set link no pap chap + set link enable chap-msv2 + set link mtu 1460 + set link keep-alive 10 60 + set ipcp yes vjcomp + set bundle enable compression + set ccp yes mppc + set ccp yes mpp-e128 + set ccp yes mpp-stateless + +EOD; + + if (!isset($pptpdcfg['req128'])) { + $mpdconf .= <<<EOD + set ccp yes mpp-e40 + set ccp yes mpp-e56 + +EOD; + } + + if (isset($config['dnsmasq']['enable'])) { + $mpdconf .= " set ipcp dns " . $config['interfaces']['lan']['ipaddr']; + if ($syscfg['dnsserver'][0]) + $mpdconf .= " " . $syscfg['dnsserver'][0]; + $mpdconf .= "\n"; + } else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) { + $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n"; + } + + if (isset($pptpdcfg['radius']['enable'])) { + $mpdconf .= <<<EOD + set radius server {$pptpdcfg['radius']['server']} "{$pptpdcfg['radius']['secret']}" + set radius retries 3 + set radius timeout 10 + set bundle enable radius-auth + set bundle disable radius-fallback + +EOD; + + if (isset($pptpdcfg['radius']['accounting'])) { + $mpdconf .= <<<EOD + set bundle enable radius-acct + +EOD; + } + } + + fwrite($fd, $mpdconf); + fclose($fd); + + /* write mpd.links */ + $fd = fopen("{$g['varetc_path']}/mpd-vpn/mpd.links", "w"); + if (!$fd) { + printf("Error: cannot open mpd.links in vpn_pptpd_configure().\n"); + return 1; + } + + $mpdlinks = ""; + + for ($i = 0; $i < $g['n_pptp_units']; $i++) { + $mpdlinks .= <<<EOD + +pt{$i}: + set link type pptp + set pptp enable incoming + set pptp disable originate + set pptp disable windowing + set pptp self 127.0.0.1 + +EOD; + } + + fwrite($fd, $mpdlinks); + fclose($fd); + + /* write mpd.secret */ + $fd = fopen("{$g['varetc_path']}/mpd-vpn/mpd.secret", "w"); + if (!$fd) { + printf("Error: cannot open mpd.secret in vpn_pptpd_configure().\n"); + return 1; + } + + $mpdsecret = ""; + + if (is_array($pptpdcfg['user'])) { + foreach ($pptpdcfg['user'] as $user) + $mpdsecret .= "{$user['name']} \"{$user['password']}\" {$user['ip']}\n"; + } + + fwrite($fd, $mpdsecret); + fclose($fd); + chmod("{$g['varetc_path']}/mpd-vpn/mpd.secret", 0600); + + /* fire up mpd */ + mwexec("/usr/local/sbin/mpd -b -d {$g['varetc_path']}/mpd-vpn -p {$g['varrun_path']}/mpd-vpn.pid pptpd"); + + break; + + case 'redir': + break; + } + + if (!$g['booting']) { + /* reload the filter */ + filter_configure(); + } + + if ($g['booting']) + echo "done\n"; + + return 0; +} + +function vpn_localnet_determine($adr, &$sa, &$sn) { + global $config, $g; + + if (isset($adr)) { + if ($adr['network']) { + switch ($adr['network']) { + case 'lan': + $sn = $config['interfaces']['lan']['subnet']; + $sa = gen_subnet($config['interfaces']['lan']['ipaddr'], $sn); + break; + } + } else if ($adr['address']) { + list($sa,$sn) = explode("/", $adr['address']); + if (is_null($sn)) + $sn = 32; + } + } else { + $sn = $config['interfaces']['lan']['subnet']; + $sa = gen_subnet($config['interfaces']['lan']['ipaddr'], $sn); + } +} + +function vpn_endpoint_determine($tunnel, $curwanip) { + + global $g, $config; + + if ((!$tunnel['interface']) || ($tunnel['interface'] == "wan")) { + if ($curwanip) + return $curwanip; + else + return null; + } else if ($tunnel['interface'] == "lan") { + return $config['interfaces']['lan']['ipaddr']; + } else { + $oc = $config['interfaces'][$tunnel['interface']]; + + if (isset($oc['enable']) && $oc['if']) { + return $oc['ipaddr']; + } + } + + return null; +} + +?> diff --git a/etc/inc/xmlparse.inc b/etc/inc/xmlparse.inc new file mode 100644 index 0000000..1fdadac --- /dev/null +++ b/etc/inc/xmlparse.inc @@ -0,0 +1,205 @@ +<?php +/* + xmlparse.inc + functions to parse/dump configuration files in XML format + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* tags that are always to be handled as lists */ +$listtags = explode(" ", "rule user key subqueue dnsserver winsserver " . + "encryption-algorithm-option hash-algorithm-option hosts tunnel onetoone " . + "staticmap route alias pipe queue shellcmd earlyshellcmd mobilekey " . + "servernat proxyarpnet passthrumac allowedip wolentry vlan"); + +function startElement($parser, $name, $attrs) { + global $depth, $curpath, $config, $havedata, $listtags; + + array_push($curpath, strtolower($name)); + + $ptr =& $config; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + + /* is it an element that belongs to a list? */ + if (in_array(strtolower($name), $listtags)) { + + /* is there an array already? */ + if (!is_array($ptr)) { + /* make an array */ + $ptr = array(); + } + + array_push($curpath, count($ptr)); + + } else if (isset($ptr)) { + /* multiple entries not allowed for this element, bail out */ + die(sprintf("XML error: %s at line %d cannot occur more than once\n", + $name, + xml_get_current_line_number($parser))); + } + + $depth++; + $havedata = $depth; +} + +function endElement($parser, $name) { + global $depth, $curpath, $config, $havedata, $listtags; + + if ($havedata == $depth) { + $ptr =& $config; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + $ptr = ""; + } + + array_pop($curpath); + + if (in_array(strtolower($name), $listtags)) + array_pop($curpath); + + $depth--; +} + +function cData($parser, $data) { + global $depth, $curpath, $config, $havedata; + + $data = trim($data, "\t\n\r"); + + if ($data != "") { + $ptr =& $config; + foreach ($curpath as $path) { + $ptr =& $ptr[$path]; + } + + if (is_string($ptr)) { + $ptr .= $data; + } else { + if (trim($data, " ") != "") { + $ptr = $data; + $havedata++; + } + } + } +} + +function parse_xml_config($cffile, $rootobj) { + + global $depth, $curpath, $config, $havedata, $listtags; + + $config = array(); + $curpath = array(); + $depth = 0; + $havedata = 0; + + $xml_parser = xml_parser_create(); + + xml_set_element_handler($xml_parser, "startElement", "endElement"); + xml_set_character_data_handler($xml_parser, "cdata"); + + if (!($fp = fopen($cffile, "r"))) { + die("Error: could not open XML input\n"); + } + + while ($data = fread($fp, 4096)) { + if (!xml_parse($xml_parser, $data, feof($fp))) { + die(sprintf("XML error: %s at line %d\n", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); + } + } + xml_parser_free($xml_parser); + + if (!$config[$rootobj]) { + die("XML error: no $rootobj object found!\n"); + } + + return $config[$rootobj]; +} + +function dump_xml_config_sub($arr, $indent) { + + global $listtags; + + $xmlconfig = ""; + + foreach ($arr as $ent => $val) { + if (is_array($val)) { + /* is it just a list of multiple values? */ + if (in_array(strtolower($ent), $listtags)) { + foreach ($val as $cval) { + if (is_array($cval)) { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent>\n"; + $xmlconfig .= dump_xml_config_sub($cval, $indent + 1); + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "</$ent>\n"; + } else { + $xmlconfig .= str_repeat("\t", $indent); + if ((is_bool($cval) && ($cval == true)) || + ($cval === "")) + $xmlconfig .= "<$ent/>\n"; + else if (!is_bool($cval)) + $xmlconfig .= "<$ent>" . htmlspecialchars($cval) . "</$ent>\n"; + } + } + } else { + /* it's an array */ + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent>\n"; + $xmlconfig .= dump_xml_config_sub($val, $indent + 1); + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "</$ent>\n"; + } + } else { + if ((is_bool($val) && ($val == true)) || ($val === "")) { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent/>\n"; + } else if (!is_bool($val)) { + $xmlconfig .= str_repeat("\t", $indent); + $xmlconfig .= "<$ent>" . htmlspecialchars($val) . "</$ent>\n"; + } + } + } + + return $xmlconfig; +} + +function dump_xml_config($arr, $rootobj) { + + $xmlconfig = "<?xml version=\"1.0\"?" . ">\n"; + $xmlconfig .= "<$rootobj>\n"; + + $xmlconfig .= dump_xml_config_sub($arr, 1); + + $xmlconfig .= "</$rootobj>\n"; + + return $xmlconfig; +} + +?> |