diff options
author | Ermal <eri@pfsense.org> | 2012-06-05 19:24:16 +0000 |
---|---|---|
committer | Ermal <eri@pfsense.org> | 2012-06-05 19:24:16 +0000 |
commit | 477cc2bc24b4b0a36b2bc765c1bb4d79a2eacaed (patch) | |
tree | 22a986b5712f3fd1f0b4d1ee8a66d2e939500c04 /etc | |
parent | 22968348b19666ec6665779f926266c88604f5ee (diff) | |
download | pfsense-477cc2bc24b4b0a36b2bc765c1bb4d79a2eacaed.zip pfsense-477cc2bc24b4b0a36b2bc765c1bb4d79a2eacaed.tar.gz |
Import OpenVPN cisco style radius attributes applying policy to logged in users. Feature #2100
Diffstat (limited to 'etc')
-rw-r--r-- | etc/inc/auth.inc | 8 | ||||
-rw-r--r-- | etc/inc/filter.inc | 2 | ||||
-rwxr-xr-x | etc/inc/openvpn.attributes.php | 186 | ||||
-rwxr-xr-x | etc/inc/openvpn.auth-user.php | 38 | ||||
-rw-r--r-- | etc/inc/openvpn.inc | 4 |
5 files changed, 234 insertions, 4 deletions
diff --git a/etc/inc/auth.inc b/etc/inc/auth.inc index 29a698c..2aeb451 100644 --- a/etc/inc/auth.inc +++ b/etc/inc/auth.inc @@ -1099,7 +1099,7 @@ function ldap_backed($username, $passwd, $authcfg) { return true; } -function radius_backed($username, $passwd, $authcfg){ +function radius_backed($username, $passwd, $authcfg, &$attributes = array()) { global $debug, $config; $ret = false; @@ -1135,6 +1135,8 @@ function radius_backed($username, $passwd, $authcfg){ if ($debug) printf("Radius send failed: %s<br>\n", $retvalue['error']); } else if ($result === true) { + if ($rauth->getAttributes()) + $attributes = $rauth->listAttributes(); $retvalue['auth_val'] = 2; if ($debug) printf(gettext("Radius Auth succeeded")."<br>\n"); @@ -1231,7 +1233,7 @@ function getUserGroups($username, $authcfg) { return $member_groups; } -function authenticate_user($username, $password, $authcfg = NULL) { +function authenticate_user($username, $password, $authcfg = NULL, &$attributes = array()) { if (!$authcfg) { return local_backed($username, $password); @@ -1244,7 +1246,7 @@ function authenticate_user($username, $password, $authcfg = NULL) { $authenticated = true; break; case 'radius': - if (radius_backed($username, $password, $authcfg)) + if (radius_backed($username, $password, $authcfg, $attributes)) $authenticated = true; break; default: diff --git a/etc/inc/filter.inc b/etc/inc/filter.inc index 13ea115..6b61651 100644 --- a/etc/inc/filter.inc +++ b/etc/inc/filter.inc @@ -2131,6 +2131,8 @@ function filter_rules_generate() { /* relayd */ $ipfrules .= "anchor \"relayd/*\"\n"; + /* OpenVPN user rules from radius */ + $ipfrules .= "anchor \"openvpn/*\"\n"; # BEGIN OF firewall rules /* default block logging? */ if(!isset($config['syslog']['nologdefaultblock'])) diff --git a/etc/inc/openvpn.attributes.php b/etc/inc/openvpn.attributes.php new file mode 100755 index 0000000..b847013 --- /dev/null +++ b/etc/inc/openvpn.attributes.php @@ -0,0 +1,186 @@ +<?php +/* + filter.inc + Copyright (C) 2011-2012 Ermal Luçi + 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. +*/ + +if (empty($common_name)) { + $common_name = getenv("common_name"); + if (empty($common_name)) + $common_name = getenv("username"); +} + +$devname = getenv("dev"); +if (empty($devname)) + $devname = "openvpn"; + +function cisco_to_cidr($addr) { + if (!is_ipaddr($addr)) + return 0; + $mask = decbin(~ip2long($addr)); + $mask = substr($mask, -32); + $k = 0; + for ($i = 0; $i <= 32; $i++) { + $k += intval($mask[$i]); + } + return $k; +} + +function cisco_extract_index($prule) { + + $index = explode("#", $prule); + if (is_numeric($index[1])) + return intval($index[1]); + else + syslog(LOG_WARNING, "Error parsing rule {$prule}: Could not extract index"); + return -1;; +} + +function parse_cisco_acl($attribs) { + global $devname, $attributes; + if (!is_array($attribs)) + return ""; + $finalrules = ""; + if (is_array($attribs['ciscoavpair'])) { + $inrules = array(); + $outrules = array(); + foreach ($attribs['ciscoavpair'] as $avrules) { + $rule = explode("=", $avrules); + $dir = ""; + if (strstr($rule[0], "inacl")) { + $dir = "in"; + } else if (strstr($rule[0], "outacl")) + $dir = "out"; + else if (strstr($rule[0], "dns-servers")) { + $attributes['dns-servers'] = explode(" ", $rule[1]); + continue; + } else if (strstr($rule[0], "route")) { + if (!is_array($attributes['routes'])) + $attributes['routes'] = array(); + $attributes['routes'][] = $route[1]; + continue; + } + $rindex = cisco_extract_index($rule[0]); + if ($rindex < 0) + continue; + + $rule = $rule[1]; + $rule = explode(" ", $rule); + $tmprule = ""; + $index = 0; + $isblock = false; + if ($rule[$index] == "permit") + $tmprule = "pass {$dir} quick on {$devname} "; + else if ($rule[$index] == "deny") { + //continue; + $isblock = true; + $tmprule = "block {$dir} quick on {$devname} "; + } else { + continue; + } + + $index++; + + switch ($rule[$index]) { + case "tcp": + case "udp": + $tmprule .= "proto {$rule[$index]} "; + break; + + } + + $index++; + /* Source */ + if (trim($rule[$index]) == "host") { + $index++; + $tmprule .= "from {$rule[$index]} "; + $index++; + if ($isblock == true) + $isblock = false; + } else if (trim($rule[$index]) == "any") { + $tmprule .= "from any"; + $index++; + } else { + $tmprule .= "from $rule[$index]"; + $index++; + $netmask = cisco_to_cidr($rule[$index]); + $tmprule .= "/{$netmask} "; + $index++; + if ($isblock == true) + $isblock = false; + } + /* Destination */ + if (trim($rule[$index]) == "host") { + $index++; + $tmprule .= "to {$rule[$index]} "; + $index++; + if ($isblock == true) + $isblock = false; + } else if (trim($rule[$index]) == "any") { + $index++; + $tmprule .= "to any"; + } else { + $tmprule .= "to $rule[$index]"; + $index++; + $netmask = cisco_to_cidr($rule[$index]); + $tmprule .= "/{$netmask} "; + $index++; + if ($isblock == true) + $isblock = false; + } + + if ($isblock == true) + continue; + + if ($dir == "in") + $inrules[$rindex] = $tmprule; + else if ($dir == "out") + $outrules[$rindex] = $tmprule; + } + + + $state = ""; + if (!empty($outrules)) + $state = "no state"; + ksort($inrules, SORT_NUMERIC); + foreach ($inrules as $inrule) + $finalrules .= "{$inrule} {$state}\n"; + if (!empty($outrules)) { + ksort($outrules, SORT_NUMERIC); + foreach ($outrules as $outrule) + $finalrules .= "{$outrule} {$state}\n"; + } + } + return $finalrules; +} + +$rules = parse_cisco_acl($attributes); +if (!empty($rules)) { + @file_put_contents("/tmp/{$common_name}.rules", $rules); + mwexec("/sbin/pfctl -a \"openvpn/{$common_name}\" -f {$g['tmp_path']}/{$common_name}.rules"); + @unlink("{$g['tmp_path']}/{$common_name}.rules"); +} + +?> diff --git a/etc/inc/openvpn.auth-user.php b/etc/inc/openvpn.auth-user.php index d36b5ff..c4f4dc7 100755 --- a/etc/inc/openvpn.auth-user.php +++ b/etc/inc/openvpn.auth-user.php @@ -96,6 +96,11 @@ if (!$username || !$password) { /* Replaced by a sed with propper variables used below(ldap parameters). */ //<template> +if (file_exists("{$g['varetc_path']}/openvpn/{$modeid}.ca")) { + putenv("LDAPTLS_CACERT={$g['varetc_path']}/openvpn/{$modeid}.ca"); + putenv("LDAPTLS_REQCERT=never"); +} + $authenticated = false; if (($strictusercn === true) && ($common_name != $username)) { @@ -103,12 +108,13 @@ if (($strictusercn === true) && ($common_name != $username)) { exit(1); } +$attributes = array(); foreach ($authmodes as $authmode) { $authcfg = auth_get_authserver($authmode); if (!$authcfg && $authmode != "local") continue; - $authenticated = authenticate_user($username, $password, $authcfg); + $authenticated = authenticate_user($username, $password, $authcfg, $attributes); if ($authenticated == true) break; } @@ -118,6 +124,36 @@ if ($authenticated == false) { exit(-1); } +if (file_exists("/etc/inc/openvpn.attributes.php")) + include_once("/etc/inc/openvpn.attributes.php"); + +$content = ""; +if (is_array($attributes['dns-servers'])) { + foreach ($attributes['dns-servers'] as $dnssrv) { + if (is_ipaddr($dnssrv)) + $content .= "push \"dhcp-option DNS {$dnssrv}\"\n"; + } +} +if (is_array($attributes['routes'])) { + foreach ($attributes['routes'] as $route) + $content .= "push \"route {$route} vpn_gateway\"\n"; +} + +if (isset($attributes['framed_ip'])) { +/* XXX: only use when TAP windows driver >= 8.2.x */ +/* if (isset($attributes['framed_mask'])) { + $content .= "topology subnet\n"; + $content .= "ifconfig-push {$attributes['framed_ip']} {$attributes['framed_mask']}"; + } else { +*/ + $content .= "topology net30\n"; + $content .= "ifconfig-push {$attributes['framed_ip']} ". long2ip((ip2long($attributes['framed_ip']) + 1)); +// } +} + +if (!empty($content)) + @file_put_contents("{$g['tmp_path']}/{$username}", $content); + syslog(LOG_WARNING, "user {$username} authenticated\n"); exit(0); diff --git a/etc/inc/openvpn.inc b/etc/inc/openvpn.inc index 9e32853..e189eac 100644 --- a/etc/inc/openvpn.inc +++ b/etc/inc/openvpn.inc @@ -364,6 +364,10 @@ function openvpn_reconfigure($mode, $settings) { $conf .= "cipher {$cipher}\n"; $conf .= "up /usr/local/sbin/ovpn-linkup\n"; $conf .= "down /usr/local/sbin/ovpn-linkdown\n"; + if (file_exists("/usr/local/sbin/openvpn.attributes.sh")) { + $conf .= "client-connect /usr/local/sbin/openvpn.attributes.sh\n"; + $conf .= "client-disconnect /usr/local/sbin/openvpn.attributes.sh\n"; + } if (!empty($iface_ip)) { $conf .= "local {$iface_ip}\n"; |