diff --git a/src/opnsense/mvc/app/cache/_users_ad_develop_deciso_opnsense_gui_app_views_index.volt.php b/src/opnsense/mvc/app/cache/_users_ad_develop_deciso_opnsense_gui_app_views_index.volt.php new file mode 100644 index 000000000..e5a7187e4 --- /dev/null +++ b/src/opnsense/mvc/app/cache/_users_ad_develop_deciso_opnsense_gui_app_views_index.volt.php @@ -0,0 +1,9 @@ + + +
+You're now flying with Phalcon. Great things are about to happen!
\ No newline at end of file diff --git a/src/opnsense/mvc/app/config/config.php b/src/opnsense/mvc/app/config/config.php new file mode 100644 index 000000000..2799364d6 --- /dev/null +++ b/src/opnsense/mvc/app/config/config.php @@ -0,0 +1,28 @@ + array( + 'adapter' => 'Mysql', + 'host' => 'localhost', + 'username' => 'root', + 'password' => '', + 'dbname' => 'test', + ), + 'application' => array( + 'controllersDir' => __DIR__ . '/../../app/controllers/', + 'modelsDir' => __DIR__ . '/../../app/models/', + 'viewsDir' => __DIR__ . '/../../app/views/', + 'pluginsDir' => __DIR__ . '/../../app/plugins/', + 'libraryDir' => __DIR__ . '/../../app/library/', + 'cacheDir' => __DIR__ . '/../../app/cache/', + 'baseUri' => '/opnsense_gui/', + ), + 'globals' => array( + 'config_path' => '/conf/', + 'temp_path' => '/tmp/', + 'vardb_path' => '/var/db/', + 'debug' => true, + 'simulate_mode' => false + + ) +)); diff --git a/src/opnsense/mvc/app/config/loader.php b/src/opnsense/mvc/app/config/loader.php new file mode 100644 index 000000000..882ee0811 --- /dev/null +++ b/src/opnsense/mvc/app/config/loader.php @@ -0,0 +1,13 @@ +registerDirs( + array( + $config->application->controllersDir, + $config->application->modelsDir + ) +)->register(); diff --git a/src/opnsense/mvc/app/config/services.php b/src/opnsense/mvc/app/config/services.php new file mode 100644 index 000000000..41a64b9af --- /dev/null +++ b/src/opnsense/mvc/app/config/services.php @@ -0,0 +1,83 @@ +set('url', function () use ($config) { + $url = new UrlResolver(); + $url->setBaseUri($config->application->baseUri); + + return $url; +}, true); + +/** + * Setting up the view component + */ +$di->set('view', function () use ($config) { + + $view = new View(); + + $view->setViewsDir($config->application->viewsDir); + + $view->registerEngines(array( + '.volt' => function ($view, $di) use ($config) { + + $volt = new VoltEngine($view, $di); + + $volt->setOptions(array( + 'compiledPath' => $config->application->cacheDir, + 'compiledSeparator' => '_' + )); + + return $volt; + }, + '.phtml' => 'Phalcon\Mvc\View\Engine\Php' + )); + + return $view; +}, true); + +/** + * Database connection is created based in the parameters defined in the configuration file + */ +$di->set('db', function () use ($config) { + return new DbAdapter(array( + 'host' => $config->database->host, + 'username' => $config->database->username, + 'password' => $config->database->password, + 'dbname' => $config->database->dbname + )); +}); + +/** + * If the configuration specify the use of metadata adapter use it or use memory otherwise + */ +$di->set('modelsMetadata', function () { + return new MetaDataAdapter(); +}); + +/** + * Start the session the first time some component request the session service + */ +$di->set('session', function () { + $session = new SessionAdapter(); + $session->start(); + + return $session; +}); + + +$di->set('config',$config); diff --git a/src/opnsense/mvc/app/controllers/ControllerBase.php b/src/opnsense/mvc/app/controllers/ControllerBase.php new file mode 100644 index 000000000..980fe7333 --- /dev/null +++ b/src/opnsense/mvc/app/controllers/ControllerBase.php @@ -0,0 +1,8 @@ +shell = new \Core\Shell(); + } + + /** + * set static arp entry + * @param $ipaddress hosts ipaddress + * @param $mac hosts physical address + */ + function setStatic($ipaddress,$mac){ + // validate input, only set static entries for valid addresses + if (preg_match('/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/', trim($mac))){ + if ( filter_var($ipaddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) ){ + $this->shell->exec("/usr/sbin/arp -s ".trim($ipaddress)." ".trim($mac)); + } + } + } + + /** + * drop static arp entry + * @param $ipaddress hosts ipaddress + */ + function dropStatic($ipaddress){ + // validate input, drop arp entry + if ( filter_var($ipaddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6) ){ + $this->shell->exec("/usr/sbin/arp -d ".trim($ipaddress) ); + } + } + + /** + * Return arp table hashed by mac address + */ + function getMACs(){ + $result = array(); + $shell_output = array(); + // execute arp shell command and collect (only valid) info into named array + if ($this->shell->exec("arp -an",false,false,$shell_output) == 0 ){ + foreach($shell_output as $line){ + $line_parts = explode(" ",$line) ; + if ( sizeof($line_parts) >= 4 ) { + $ipaddress = substr($line_parts[1],1,strlen($line_parts[1])-2 ) ; + // reformat mac addresses, sometimes arp return segments without trailing zero's + $mac_raw = strtolower($line_parts[3]); + $mac = ""; + foreach(explode(":",$mac_raw) as $segment ){ + if ( $mac != "") $mac .= ":"; + if (strlen($segment) == 1) $mac .= "0".$segment; + else $mac .= $segment ; + } + if (preg_match('/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/', trim($mac))){ + $result[$mac]= array('ip'=>$ipaddress); + } + } + } + } + return $result; + } + + +} \ No newline at end of file diff --git a/src/opnsense/mvc/app/models/Captiveportal/CPClient.php b/src/opnsense/mvc/app/models/Captiveportal/CPClient.php new file mode 100644 index 000000000..f16277d43 --- /dev/null +++ b/src/opnsense/mvc/app/models/Captiveportal/CPClient.php @@ -0,0 +1,500 @@ +config->object()->captiveportal->children() as $zone => $zoneobj){ + if ($zone == $cpzonename) $zoneid = $zoneobj->zoneid; + } + + if ($zoneid == -1) return; // not a valid zone + + $db = new DB($cpzonename); + $db_clients = $db->listClients(array("sessionid"=>$sessionid)); + + $ipfw_tables = $this->rules->getAuthUsersTables($zoneid); + if ( sizeof($db_clients) > 0 ){ + if ($db_clients->ip != null ) { + // only handle disconnect if we can find a client in our database + $exec_commands[] = "/sbin/ipfw table " . $ipfw_tables["in"] . " delete " . $db_clients[0]->ip; + $exec_commands[] = "/sbin/ipfw table " . $ipfw_tables["out"] . " delete " . $db_clients[0]->ip; + $this->shell->exec($exec_commands, false, false); + // TODO: cleanup dummynet pipes $db_clients[0]->pipeno_in/out + // TODO: log removal ( was : captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");) + } + $db->remove_session($sessionid); + } + } + + /** + * + * @param $zoneid + * @param $ip + */ + public function add_accounting($zoneid,$ip){ + // TODO: check speed, this might need some improvement + // check if our ip is already in the list and collect first free rule number to place it there if necessary + $shell_output=array(); + $this->shell->exec("/sbin/ipfw show",false,false,$shell_output); + $prev_id = 0; + $new_id = null; + foreach($shell_output as $line){ + // only trigger on counter rules and last item in the list + if ( strpos($line," count " ) !== false || strpos($line,"65535 " )!== false ) { + if ( strpos($line," ".$ip." " ) !== false ) { + // already in table... exit + return; + } + + $this_line_id = (int) (explode(" ",$line)[0]) ; + if ( $this_line_id > 30000 and ($this_line_id -1) > $prev_id and $new_id == null) { + // new id found + if ( $this_line_id == 65535 ) $new_id = $prev_id+1; + else $new_id = $this_line_id-1; + } + + $prev_id = $this_line_id; + } + } + + if ( $new_id != null ) { + $exec_commands = array( + "/sbin/ipfw add " . $new_id . " set " . $zoneid . " count ip from " . $ip . " to any ", + "/sbin/ipfw add " . $new_id . " set " . $zoneid . " count ip from any to " . $ip, + ); + + // execute all ipfw actions + $this->shell->exec($exec_commands, false, false); + } + } + + /** + * Constructor + */ + function __construct() { + // Request handle to configuration + $this->config = \Core\Config::getInstance(); + // generate new ruleset + $this->rules = new \Captiveportal\Rules(); + // keep a link to the shell object + $this->shell = new \Core\Shell(); + } + + /** + * Reconfigure zones ( generate and load ruleset ) + */ + public function reconfigure(){ + $ruleset_filename = \Phalcon\DI\FactoryDefault::getDefault()->get('config')->globals->temp_path."/ipfw.rules"; + $this->rules->generate($ruleset_filename); + + // load ruleset + $this->shell->exec("/sbin/ipfw -f ".$ruleset_filename); + + // update tables + $this->update_config(); + } + + /** + * update zone(s) with new configuration data + * @param string $zone + */ + public function update($zone=null){ + $this->refresh_allowed_ips($zone); + $this->refresh_allowed_mac($zone); + } + + /** + * refresh allowed ip's for defined zone ( null for all zones ) + * @param string $zone + */ + public function refresh_allowed_ips($cpzone=null){ + + $handled_addresses = array(); + foreach( $this->config->object()->captiveportal->children() as $cpzonename => $zone ) { + // search requested zone (id) + if ( $cpzonename == $cpzone || $zone->zoneid == $cpzone || $cpzone == null ) { + $db = new DB($cpzonename); + $db_iplist = $db->listFixedIPs(); + + // calculate table numbers for this zone + $ipfw_tables = $this->rules->getAuthIPTables($zone->zoneid ); + + foreach ($zone->children() as $tagname => $tagcontent) { + $ip = $tagcontent->ip->__toString(); + if ($tagname == 'allowedip') { + $handled_addresses[$ip] = array(); + $handled_addresses[$ip]["bw_up"] = $tagcontent->bw_up->__toString() ; + $handled_addresses[$ip]["bw_down"] = $tagcontent->bw_down->__toString() ; + + if ( !array_key_exists($ip,$db_iplist) ){ + // only insert new values + $pipeno_in = $this->new_ipfw_pipeno() ; + $pipeno_out = $this->new_ipfw_pipeno() ; + + $exec_commands = array( + # insert new ip address + "/sbin/ipfw table ". $ipfw_tables["in"] ." add " . $ip . "/" . $tagcontent->sn->__toString() . " " . $pipeno_in, + "/sbin/ipfw table ". $ipfw_tables["out"] ." add " . $ip . "/" . $tagcontent->sn->__toString() . " " . $pipeno_out, + ); + + // execute all ipfw actions + $this->shell->exec($exec_commands, false,false); + // update administration + $db->upsertFixedIP($ip,$pipeno_in,$pipeno_out); + // save bandwidth data + $handled_addresses[$ip]["pipeno_in"] = $pipeno_in ; + $handled_addresses[$ip]["pipeno_out"] = $pipeno_out ; + }else{ + // + $handled_addresses[$ip]["pipeno_in"] = $db_iplist[$ip]->pipeno_in ; + $handled_addresses[$ip]["pipeno_out"] = $db_iplist[$ip]->pipeno_out ; + } + } + + } + + + // Cleanup deleted addresses + foreach($db_iplist as $ip => $record){ + if (!array_key_exists($ip,$handled_addresses)){ + $exec_commands = array( + # insert new ip address + "/sbin/ipfw table ". $ipfw_tables["in"] ." del " . $ip . "/" . $tagcontent->sn->__toString() , + "/sbin/ipfw table ". $ipfw_tables["out"] ." del " . $ip . "/" . $tagcontent->sn->__toString() , + ); + + // execute all ipfw actions + $this->shell->exec($exec_commands, false,false); + // TODO : cleanup $record->pipeno_in, $record->pipeno_out ; + $db->dropFixedIP($ip); + } + } + + // reset bandwidth, + foreach($handled_addresses as $mac => $record){ + if (array_key_exists("pipeno_in",$record) ){ + $this->reset_bandwidth($record["pipeno_in"],$record["bw_down"]); + $this->reset_bandwidth($record["pipeno_out"],$record["bw_up"]); + } + } + + unset($db); + } + } + + } + + /** + * To be able to grant access to physical pc's, we need to do some administration. + * Our captive portal database keeps a list of every used address and last know mac address + * + * @param String $zone zone name or number + */ + public function refresh_allowed_mac($cpzone=null){ + + // read ARP table + $arp= new ARP(); + $arp_maclist = $arp->getMACs(); + + // keep a list of handled addresses, so we can cleanup the rest and keep track of needed bandwidth restrictions + $handled_mac_addresses = array(); + foreach( $this->config->object()->captiveportal->children() as $cpzonename => $zone ) { + if ( $cpzonename == $cpzone || $zone->zoneid == $cpzone || $cpzone == null ) { + // open administrative database for this zone + $db = new DB($cpzonename); + $db_maclist = $db->listPassthruMacs(); + $ipfw_tables = $this->rules->getAuthMACTables($zone->zoneid); + + foreach ($zone->children() as $tagname => $tagcontent) { + $mac = trim(strtolower($tagcontent->mac)); + if ($tagname == 'passthrumac') { + // only accept valid macaddresses + if (preg_match('/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/', $mac)) { + if ($tagcontent->action == "pass") { + $handled_mac_addresses[$mac] = array("action"=>"skipped" ); + $handled_mac_addresses[$mac]["bw_up"] = $tagcontent->bw_up ; + $handled_mac_addresses[$mac]["bw_down"] = $tagcontent->bw_down ; + + // only handle addresses we know of + if (array_key_exists($mac, $arp_maclist)) { + // if the address is already in our database, check if it has changed + if ( array_key_exists($mac,$db_maclist) ) { + // save pipe numbers for bandwidth restriction + $handled_mac_addresses[$mac]["pipeno_in"] = $db_maclist[$mac]->pipeno_in ; + $handled_mac_addresses[$mac]["pipeno_out"] = $db_maclist[$mac]->pipeno_out ; + + if ($db_maclist[$mac]->ip != $arp_maclist[$mac]['ip'] ) { + // handle changed ip, + $handled_mac_addresses[$mac]["action"] = "changed ip"; + $exec_commands = array( + # delete old ip address + "/sbin/ipfw table ". $ipfw_tables["in"] ." delete ". $db_maclist[$mac]->ip, + "/sbin/ipfw table ". $ipfw_tables["out"] ." delete ". $db_maclist[$mac]->ip, + # insert new ip address + "/sbin/ipfw table ". $ipfw_tables["in"] ." add " . $arp_maclist[$mac]['ip']. " " . $db_maclist[$mac]->pipeno_in, + "/sbin/ipfw table ". $ipfw_tables["out"] ." add " . $arp_maclist[$mac]['ip']. " " . $db_maclist[$mac]->pipeno_out, + ); + + // execute all ipfw actions + $this->shell->exec($exec_commands, false,false); + // update administration + $db->upsertPassthruMAC($tagcontent->mac,$arp_maclist[$mac]['ip'],$db_maclist[$mac]->pipeno_in,$db_maclist[$mac]->pipeno_out); // new ip according to arp table + } + } + else { + // new host, not seen it yet + $handled_mac_addresses[$mac]["action"] = "new"; + $pipeno_in = $this->new_ipfw_pipeno() ; + $pipeno_out = $this->new_ipfw_pipeno() ; + + // execute all ipfw actions + $exec_commands = array( + # insert new ip address + "/sbin/ipfw table ". $ipfw_tables["in"] ." add " . $arp_maclist[$mac]['ip']. " " . $pipeno_in, + "/sbin/ipfw table ". $ipfw_tables["out"] ." add " . $arp_maclist[$mac]['ip']. " " . $pipeno_out, + ); + $this->shell->exec($exec_commands, false,false); + + $db->upsertPassthruMAC($tagcontent->mac,$arp_maclist[$mac]['ip'],$pipeno_in,$pipeno_out); + // save pipe numbers for bandwidth restriction + $handled_mac_addresses[$mac]["pipeno_in"] = $pipeno_in ; + $handled_mac_addresses[$mac]["pipeno_out"] = $pipeno_out ; + } + } + } + } + } + } + + // + // cleanup old addresses + // + foreach($db_maclist as $mac => $record){ + if ( !array_key_exists($mac,$handled_mac_addresses) ){ + # delete old ip address, execute all actions + $exec_commands = array( + "/sbin/ipfw table ". $ipfw_tables["in"] ." delete ". $db_maclist[$mac]->ip, + "/sbin/ipfw table ". $ipfw_tables["out"] ." delete ". $db_maclist[$mac]->ip, + ); + $this->shell->exec($exec_commands, false,false); + // TODO : cleanup $record->pipeno_in, $record->pipeno_out ; + $db->dropPassthruMAC($mac); + } + } + + // reset bandwidth + foreach($handled_mac_addresses as $mac => $record){ + if (array_key_exists("pipeno_in",$record) ){ + $this->reset_bandwidth($record["pipeno_in"],$record["bw_down"]); + $this->reset_bandwidth($record["pipeno_out"],$record["bw_up"]); + } + } + + unset($db); + + } + } + + } + + /** + * @param string $cpzonename + * @param string $clientip + * @param string $clientmac + * @param string $username + * @param string $password + * @param string $attributes + * @param string $radiusctx + */ + public function portal_allow($cpzonename,$clientip,$clientmac,$username,$password = null,$bw_up=null,$bw_down=null, $attributes = null, $radiusctx = null){ + // defines + $exec_commands = array() ; + $db = new DB($cpzonename); + $arp= new ARP(); + + // find zoneid for this named zone + $zoneid = -1; + foreach($this->config->object()->captiveportal->children() as $zone => $zoneobj){ + if ($zone == $cpzonename) $zoneid = $zoneobj->zoneid; + } + + if ($zoneid == -1) return; // not a valid zone, bailout + + + // grap needed data to generate our rules + $ipfw_tables = $this->rules->getAuthUsersTables($zoneid); + $cp_table = $db->listClients(array("mac"=>$clientmac,"ip"=>$clientip),"or"); + if ( sizeof($cp_table) > 0 && ($cp_table[0]->ip == $clientip && $cp_table[0]->mac == $clientmac ) ){ + // nothing (important) changed here... move on + return; + } elseif ( sizeof($cp_table) > 0) { + // something changed... + // prevent additional sessions to popup, one MAC should have only one active session, remove the rest (if any) + $cnt = 0; + $remove_sessions = array(); + foreach($cp_table as $record){ + if ( $cnt >0) $remove_sessions[] = $record->sessionid; + else $current_session = $record; + $cnt++; + // prepare removal for all ip addresses belonging to this host + $exec_commands[] = "/sbin/ipfw table ". $ipfw_tables["in"] ." delete ". $record->ip; + $exec_commands[] = "/sbin/ipfw table ". $ipfw_tables["out"] ." delete ". $record->ip; + // TODO: if for some strange reason there is more than one session, we are failing to drop the pipes + $exec_commands[] = "/usr/sbin/arp -d ".trim($record->ip); // drop static arp entry (prevent MAC change) + } + if (sizeof($remove_sessions)){ + $db->remove_session($remove_sessions); + } + + // collect pipe numbers for dummynet + $pipeno_in = $current_session->pipeno_in; + $pipeno_out = $current_session->pipeno_out; + + $db->update_session($current_session->sessionid,array("ip"=>$clientip,"mac"=>$clientmac)); + } else + { + // new session, allocate new dummynet pipes and generate a unique id + $pipeno_in = $this->new_ipfw_pipeno(); + $pipeno_out = $this->new_ipfw_pipeno(); + + // construct session data + $session_data=Array(); + $session_data["ip"]=$clientip; + $session_data["mac"]=$clientmac; + $session_data["pipeno_in"] = $pipeno_in; + $session_data["pipeno_out"] = $pipeno_out; + $session_data["username"]=\SQLite3::escapeString($username); + $session_data["bpassword"] =base64_encode($password); + $session_data["session_timeout"] = -1; + $session_data["idle_timeout"] = -1; + $session_data["session_terminate_time"] = -1; + $session_data["interim_interval"] = -1; + $session_data["radiusctx"] = $radiusctx; + $session_data["allow_time"] = time(); // allow time is actual starting time of this session + $sessionid = uniqid() ; + + $db->insert_session($sessionid, $session_data ); + + } + + // add commands for access tables, and execute all collected + $exec_commands[] = "/sbin/ipfw table ". $ipfw_tables["in"] ." add ". $clientip . " ".$pipeno_in; + $exec_commands[] = "/sbin/ipfw table ". $ipfw_tables["out"] ." add ". $clientip . " ".$pipeno_out; + $this->shell->exec($exec_commands, false,false); + + // lock the user/ip to it's MAC address using arp + $arp->setStatic($clientip,$clientmac); + + // add accounting rule + $this->add_accounting($zoneid,$clientip); + + // cleanup + unset($db); + } + + + /** + * disconnect a session or a list of sessions depending on the parameter + * @param string $cpzonename zone name or id + * @param $sessionid + */ + public function disconnect($cpzonename,$sessionid){ + if ( is_array($sessionid)){ + foreach($sessionid as $sessid ){ + $this->_disconnect($cpzonename,$sessid); + } + } + else{ + $this->_disconnect($cpzonename,$sessionid); + } + } + + /** + * flush zone (null flushes all zones) + * @param null $zone + */ + function flush($zone=null){ + if ( $zone == null ) { + $shell = new \Core\Shell(); + $shell->exec("/sbin/ipfw -f table all flush"); + } + else{ + // find zoneid for this named zone + if (preg_match("/^[0-9]{1,2}$/", trim($zone)) ) { + $zoneid = $zone; + }else { + $zoneid = -1; + foreach ($this->config->object()->captiveportal->children() as $zonenm => $zoneobj) { + if ($zonenm == $zone) $zoneid = $zoneobj->zoneid; + } + } + + if ( $zoneid != -1 ){ + $exec_commands= array( + "/sbin/ipfw -f table ".$this->rules->getAuthUsersTables($zoneid)["in"]." flush", + "/sbin/ipfw -f table ".$this->rules->getAuthUsersTables($zoneid)["out"]." flush", + "/sbin/ipfw -f table ".$this->rules->getAuthIPTables($zoneid)["in"]." flush", + "/sbin/ipfw -f table ".$this->rules->getAuthIPTables($zoneid)["out"]." flush", + "/sbin/ipfw -f table ".$this->rules->getAuthMACTables($zoneid)["in"]." flush", + "/sbin/ipfw -f table ".$this->rules->getAuthMACTables($zoneid)["out"]." flush", + "/sbin/ipfw delete set ".$zoneid, + ); + $this->shell->exec($exec_commands, false,false); + } + } + } + + + +} diff --git a/src/opnsense/mvc/app/models/Captiveportal/DB.php b/src/opnsense/mvc/app/models/Captiveportal/DB.php new file mode 100644 index 000000000..49f676ca7 --- /dev/null +++ b/src/opnsense/mvc/app/models/Captiveportal/DB.php @@ -0,0 +1,327 @@ + \PDO::PARAM_INT, + "pipeno_in" => \PDO::PARAM_INT, + "pipeno_out" => \PDO::PARAM_INT, + "ip" => \PDO::PARAM_STR, + "mac" => \PDO::PARAM_STR, + "username" => \PDO::PARAM_STR, + "sessionid" => \PDO::PARAM_STR, + "bpassword" => \PDO::PARAM_STR, + "session_timeout" => \PDO::PARAM_INT, + "idle_timeout" => \PDO::PARAM_INT, + "session_terminate_time" => \PDO::PARAM_INT, + "interim_interval" => \PDO::PARAM_INT, + "radiusctx" => \PDO::PARAM_STR); + + /** + * datatypes for captive portal mac table + * @var array + */ + private $captiveportal_mac_types=array( + "mac" => \PDO::PARAM_STR, + "ip" => \PDO::PARAM_STR, + "pipeno_in" => \PDO::PARAM_INT, + "pipeno_out" => \PDO::PARAM_INT, + "last_checked" => \PDO::PARAM_INT); + + /** + * datatypes for captive portal ip table + * @var array + */ + private $captiveportal_ip_types=array( + "ip" => \PDO::PARAM_STR, + "pipeno_in" => \PDO::PARAM_INT, + "pipeno_out" => \PDO::PARAM_INT, + "last_checked" => \PDO::PARAM_INT); + + /** + * open / create new captive portal database for zone + * @param $zone zone name + */ + function __construct($zone) + { + $this->zone = $zone ; + $this->open(); + } + + /** + * destruct, close sessions + */ + function __destruct() { + if ( $this->handle != null){ + $this->handle->close(); + } + } + + /** + * open database, on failure send message tot syslog + * creates structure needed for this captiveportal zone + * @return SQLite3 + */ + function open(){ + // open database + $db_path = \Phalcon\DI\FactoryDefault::getDefault()->get('config')->globals->vardb_path ."/captiveportal".$this->zone.".db" ; + try { + $this->handle = new \Phalcon\Db\Adapter\Pdo\Sqlite(array("dbname" => $db_path)); + + // create structure on new database + if (!$this->handle->execute("CREATE TABLE IF NOT EXISTS captiveportal (" . # table used for authenticated users + "allow_time INTEGER, pipeno_in INTEGER, pipeno_out INTEGER, ip TEXT, mac TEXT, username TEXT, " . + "sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " . + "session_terminate_time INTEGER, interim_interval INTEGER, radiusctx TEXT); " . + "CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " . + "CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " . + "CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " . + "CREATE INDEX IF NOT EXISTS starttime ON captiveportal (allow_time);". + "CREATE TABLE IF NOT EXISTS captiveportal_mac (" . # table used for static mac's + "mac TEXT, ip TEXT,pipeno_in INTEGER, pipeno_out INTEGER, last_checked INTEGER );" . + "CREATE UNIQUE INDEX IF NOT EXISTS idx_mac ON captiveportal_mac (mac) ;". + "CREATE TABLE IF NOT EXISTS captiveportal_ip (" . # table used for static ip's + "ip TEXT,pipeno_in INTEGER, pipeno_out INTEGER, last_checked INTEGER );" . + "CREATE UNIQUE INDEX IF NOT EXISTS idx_ip ON captiveportal_ip (ip) " + ) + ) { + + $logger = new \Phalcon\Logger\Adapter\Syslog("logportalauth", array( + 'option' => LOG_PID, + 'facility' => LOG_LOCAL4 + )); + $logger->error("Error during table {$this->zone} creation. Error message: {$this->handle->lastErrorMsg()}"); + $this->handle = null ; + } + + + }catch (\Exception $e) { + $logger = new \Phalcon\Logger\Adapter\Syslog("logportalauth", array( + 'option' => LOG_PID, + 'facility' => LOG_LOCAL4 + )); + $logger->error("Error opening database for zone " . $this->zone . " : ".$e->getMessage()." "); + $this->handle = null ; + } + + return $this->handle; + + } + + /** + * remove session(s) from database + * @param $sessionids session ids ( or id ) + */ + function remove_session($sessionids){ + if ( $this->handle != null ){ + if ( is_array($sessionids) ) $tmpids = $sessionids; + else $tmpids = array($sessionids); + + $this->handle->begin() ; + $stmt = $this->handle->prepare('DELETE FROM captiveportal WHERE sessionid = :sessionid'); + foreach( $tmpids as $session ) { + $this->handle->executePrepared($stmt, array('sessionid' => $session),array("sessionid"=>\PDO::PARAM_STR)); + $stmt->execute(); + } + $this->handle->commit() ; + + } + } + + /** + * + * @param string $sessionid session id + * @param Array() $content data to alter ( fields from "captiveportal") + */ + function update_session($sessionid,$content){ + if ( $this->handle != null ) { + $query = "update captiveportal set "; + $bind_values = Array("sessionid" => $sessionid); + foreach ($content as $fieldname => $fieldvalue) { + // you may not alter data not described in $this->captiveportal_types + if (array_key_exists($fieldname, $this->captiveportal_types)) { + if (sizeof($bind_values) > 1) $query .= " , "; + $query .= $fieldname." = "." :".$fieldname." "; + $bind_values[$fieldname] = $fieldvalue; + } + } + $query .= " where sessionid = :sessionid "; + try { + $this->handle->execute($query, $bind_values, $this->captiveportal_types); + } catch (\Exception $e) { + $logger = new \Phalcon\Logger\Adapter\Syslog("logportalauth", array( + 'option' => LOG_PID, + 'facility' => LOG_LOCAL4 + )); + $logger->error("Trying to modify DB returned error (zone = " . $this->zone . " ) : " . $e->getMessage() . " "); + } + } + } + + /** + * insert new session information into this zone's database + * + * @param string $sessionid unique session id + * @param Array() field content ( defined fields in "captiveportal") + */ + function insert_session($sessionid,$content){ + if ( $this->handle != null ) { + // construct insert query, using placeholders for bind variables + $bind_values = Array("sessionid" => $sessionid); + $query = "insert into captiveportal (sessionid "; + $query_values = "values (:sessionid "; + foreach ($content as $fieldname => $fieldvalue) { + // you may not alter data not described in $this->captiveportal_types + if (array_key_exists($fieldname, $this->captiveportal_types)) { + $query .= "," . $fieldname . " "; + $query_values .= ", :" . $fieldname; + $bind_values[$fieldname] = $fieldvalue; + } + } + $query .= " ) " . $query_values . ") "; + try { + $this->handle->execute($query, $bind_values, $this->captiveportal_types); + } catch (\Exception $e) { + $logger = new \Phalcon\Logger\Adapter\Syslog("logportalauth", array( + 'option' => LOG_PID, + 'facility' => LOG_LOCAL4 + )); + $logger->error("Trying to modify DB returned error (zone = " . $this->zone . " ) : " . $e->getMessage() . " "); + } + } + } + + + /** + * get captive portal clients + * @param Array() $args + */ + function listClients($qryargs,$operator="and"){ + // construct query, only parse fields defined by $this->captiveportal_types + $qry_tag = "where " ; + $query = "select * from captiveportal "; + foreach ( $qryargs as $fieldname => $fieldvalue ){ + if ( array_key_exists($fieldname,$this->captiveportal_types) ){ + $query .= $qry_tag . $fieldname." = "." :".$fieldname." "; + $qry_tag = " ".$operator." "; + } + } + + $resultset = $this->handle->query($query, $qryargs, $this->captiveportal_types); + $resultset->setFetchMode(\Phalcon\Db::FETCH_OBJ); + + return $resultset->fetchAll(); + } + + /** + * list all fixed ip addresses for this zone + * + * @return Array() + */ + function listFixedIPs(){ + $result = array(); + if ($this->handle != null ) { + $resultset = $this->handle->query("select ip,pipeno_in,pipeno_out,last_checked from captiveportal_ip"); + $resultset->setFetchMode(\Phalcon\Db::FETCH_OBJ); + + foreach ($resultset->fetchAll() as $record) { + $result[$record->ip] = $record; + } + } + + return $result; + } + + /** + * insert new passthru mac address + * @param $ip hosts ip address + */ + function upsertFixedIP($ip,$pipeno_in=null,$pipeno_out=null){ + // perform an upsert to update the data for this physical host. + // unfortunately this costs an extra write io for the first record, but provides cleaner code + $params = array("ip"=>$ip,"pipeno_in"=>$pipeno_in,"pipeno_out"=>$pipeno_out,"last_checked"=>time()); + $this->handle->execute("insert or ignore into captiveportal_ip(ip) values (:ip)", array("ip"=>$ip),$this->captiveportal_ip_types); + $this->handle->execute("update captiveportal_ip set ip=:ip, last_checked=:last_checked, pipeno_in = :pipeno_in, pipeno_out = :pipeno_out where ip =:ip ", $params,$this->captiveportal_ip_types); + } + + /** + * drop address from administration (captiveportal_ip) + * @param $mac physical address + */ + function dropFixedIP($ip){ + $this->handle->execute("delete from captiveportal_ip where ip =:ip ", array("ip"=>$ip),$this->captiveportal_ip_types); + } + + + /** + * list all passthru mac addresses for this zone + * + * @return Array() + */ + function listPassthruMacs(){ + $result = array(); + if ($this->handle != null ) { + $resultset = $this->handle->query("select mac,ip,last_checked,pipeno_in,pipeno_out from captiveportal_mac"); + $resultset->setFetchMode(\Phalcon\Db::FETCH_OBJ); + + foreach ($resultset->fetchAll() as $record) { + $result[$record->mac] = $record; + } + } + + return $result; + } + + + /** + * insert new passthru mac address + * @param $mac physical address + * @param $ip hosts ip address + */ + function upsertPassthruMAC($mac,$ip,$pipeno_in=null,$pipeno_out=null){ + // perform an upsert to update the data for this physical host. + // unfortunately this costs an extra write io for the first record, but provides cleaner code + $params = array("mac"=>$mac,"ip"=>$ip,"pipeno_in"=>$pipeno_in,"pipeno_out"=>$pipeno_out,"last_checked"=>time()); + $this->handle->execute("insert or ignore into captiveportal_mac(mac) values (:mac)", array("mac"=>$mac),$this->captiveportal_mac_types); + $this->handle->execute("update captiveportal_mac set ip=:ip, last_checked=:last_checked, pipeno_in = :pipeno_in, pipeno_out = :pipeno_out where mac =:mac ", $params,$this->captiveportal_mac_types); + } + + /** + * drop address from administration (captiveportal_mac) + * @param $mac physical address + */ + function dropPassthruMAC($mac){ + $this->handle->execute("delete from captiveportal_mac where mac =:mac ", array("mac"=>$mac),$this->captiveportal_mac_types); + } + + + + +} \ No newline at end of file diff --git a/src/opnsense/mvc/app/models/Captiveportal/Rules.php b/src/opnsense/mvc/app/models/Captiveportal/Rules.php new file mode 100644 index 000000000..e08e67b5f --- /dev/null +++ b/src/opnsense/mvc/app/models/Captiveportal/Rules.php @@ -0,0 +1,296 @@ +config = \Core\Config::getInstance(); + } + + + /** + * get ipfw tables for authenticated users ( in/out ) + * @param $zoneid zoneid (number) + * @return array + */ + function getAuthUsersTables($zoneid){ + return array("in"=>(6*($zoneid-1) )+1,"out"=>(6*($zoneid-1) )+2); + } + + /** + * get ipfw tables for authenticated hosts ( in/out ) + * @param $zoneid zoneid (number) + * @return array + */ + function getAuthIPTables($zoneid){ + return array("in"=>(6*($zoneid-1) )+3,"out"=>(6*($zoneid-1) )+4); + } + + /** + * get ipfw tables used for authenticated physical addresses + * @param $zoneid zoneid (number) + * @return array + */ + function getAuthMACTables($zoneid){ + return array("in"=>(6*($zoneid-1) )+5,"out"=>(6*($zoneid-1) )+6); + } + + + /** + * default rules + * rule number range 1..1000 + */ + private function generate_default_rules(){ + // define general purpose rules, rule number 1 .... 1000 + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# flush ruleset "; + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "flush" ; + + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# general purpose rules 1...1000 "; + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "add 100 allow pfsync from any to any"; + $this->rules[] = "add 110 allow carp from any to any"; + $this->rules[] = "# layer 2: pass ARP"; + $this->rules[] = "add 120 pass layer2 mac-type arp,rarp"; + $this->rules[] = "# OPNsense requires for WPA"; + $this->rules[] = "add 130 pass layer2 mac-type 0x888e,0x88c7"; + $this->rules[] = "# PPP Over Ethernet Session Stage/Discovery Stage"; + $this->rules[] = "add 140 pass layer2 mac-type 0x8863,0x8864"; + $this->rules[] = "# layer 2: block anything else non-IP(v4/v6)"; + $this->rules[] = "add 150 deny layer2 not mac-type ip,ipv6"; + + } + + /** + * Always allow traffic to our own host ( all static addresses from configuration ) + * rule number range 1001..1999 + */ + private function generate_this_host_rules(){ + // search all static / non wan addresses and add rules to $this->rules + $rulenum = 1001 ; + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# Allow traffic to this hosts static ip's ( 1001..1999 ) "; + $this->rules[] = "#========================================================================================================="; + foreach( $this->config->object()->interfaces->children() as $interface => $content ){ + if ( $interface != "wan" && $content->ipaddr != "dhcp" ){ + $this->rules[] = "add ".$rulenum++." allow ip from any to { 255.255.255.255 or ".$content->ipaddr." } in"; + $this->rules[] = "add ".$rulenum++." allow ip from { 255.255.255.255 or ".$content->ipaddr." } to any out"; + $this->rules[] = "add ".$rulenum++." allow icmp from { 255.255.255.255 or ".$content->ipaddr." } to any out icmptypes 0"; + $this->rules[] = "add ".$rulenum++." allow icmp from any to { 255.255.255.255 or ".$content->ipaddr." } in icmptypes 8"; + } + } + } + + + /** + * generate zone rules, 4 ipfw tables per zone ( in/out, by host or address ) + * The tables are calculcated by zoneid using the getAuthxxxTables methods : + * 1. authenticated users in + * 2. authenticated users out + * 3. allowed ip's in + * 4. allowed ip's out + * 5. allowed mac addresses in ( table contains corresponding ip's ) + * 6. allowed mac addresses out ( table contains corresponding ip's ) + * + * A pipe to dummynet is automatically created for every stream + * + * Every zone receives it's own ruleset range of max 998 rules, defined by a starting position of 10.000 + * ( for example: zone 2 starts @ 12000 ) + * + * rule number ranges 3001..3999, 10000...50000 + */ + private function generate_zones(){ + foreach( $this->config->object()->captiveportal->children() as $cpzonename => $zone ) { + // search interface + $interface = $zone->interface->xpath("//" . $zone->interface); + + // allocate tables for captive portal + $table_id = (6*($zone->zoneid-1) ); + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# zone " . $cpzonename . " (".$zone->zoneid.") configuration"; + $this->rules[] = "#========================================================================================================="; + + if (count($interface) > 0) { + $interface = $interface[0]; + // authenticated users ( table 1 + 2 ) + $this->rules[] = "add ".(3000+($zone->zoneid*10)+1)." skipto ".((($zone->zoneid*1000)+10000)+1)." ip from table(".$this->getAuthUsersTables($zone->zoneid)["in"].") to any via ".$interface->if ; + $this->rules[] = "add ".(3000+($zone->zoneid*10)+2)." skipto ".((($zone->zoneid*1000)+10000)+1)." ip from any to table(".$this->getAuthUsersTables($zone->zoneid)["in"].") via ".$interface->if ; + + // authenticated hosts ( table 3 + 4 ) + $this->rules[] = "add ".(3000+($zone->zoneid*10)+3)." skipto ".((($zone->zoneid*1000)+10000)+1)." ip from table(".$this->getAuthIPTables($zone->zoneid)["in"].") to any via ".$interface->if ; + $this->rules[] = "add ".(3000+($zone->zoneid*10)+4)." skipto ".((($zone->zoneid*1000)+10000)+1)." ip from any to table(".$this->getAuthIPTables($zone->zoneid)["in"].") via ".$interface->if ; + + // authenticated mac addresses ( table 5 + 6 ) + $this->rules[] = "add ".(3000+($zone->zoneid*10)+5)." skipto ".((($zone->zoneid*1000)+10000)+1)." ip from table(".$this->getAuthMACTables($zone->zoneid)["in"].") to any via ".$interface->if ; + $this->rules[] = "add ".(3000+($zone->zoneid*10)+6)." skipto ".((($zone->zoneid*1000)+10000)+1)." ip from any to table(".$this->getAuthMACTables($zone->zoneid)["in"].") via ".$interface->if ; + +// TODO: solve dummynet kernel issue on outgoing traffic +// // dummynet 1,2 +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+1)." pipe tablearg ip from table(".($table_id+1).") to any in via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+2)." pipe tablearg ip from any to table(".($table_id+2).") out via ".$interface->if ; +// +// // dummynet 3,4 +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+3)." pipe tablearg ip from table(".($table_id+3).") to any in via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+4)." pipe tablearg ip from table(".($table_id+3).") to any out via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+5)." pipe tablearg ip from any to table(".($table_id+4).") in via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+6)." pipe tablearg ip from any to table(".($table_id+4).") out via ".$interface->if ; + +// // dummynet 5,6 +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+7)." pipe tablearg ip from table(".($table_id+5).") to any in via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+8)." pipe tablearg ip from table(".($table_id+5).") to any out via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+9)." pipe tablearg ip from any to table(".($table_id+6).") in via ".$interface->if ; +// $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+10)." pipe tablearg ip from any to table(".($table_id+6).") out via ".$interface->if ; + + // statistics for this zone, placeholder to jump to + $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+1)." count ip from any to any via ".$interface->if ; + + // jump to accounting section + $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+998)." skipto 30000 all from any to any via ".$interface->if ; + $this->rules[] = "add ".((($zone->zoneid*1000)+10000)+999)." deny all from any to any not via ".$interface->if ; + + } + } + + } + + /** + * Forward all non authenticated traffic from captive portal zones + * rule number range 5001..5999 + */ + private function generate_reflect_rules(){ + $forward_port = 8000 ; + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# redirect non-authenticated clients to captive portal @ local port ".$forward_port." + zoneid "; + $this->rules[] = "#========================================================================================================="; + foreach( $this->config->object()->captiveportal->children() as $cpzonename => $zone ){ + // search interface + $interface = $zone->interface->xpath("//".$zone->interface); + if (count($interface) > 0){ + $interface = $interface[0] ; + if ($interface->ipaddr != null){ + // add forward rule to this zone's http instance @ $forward_port + $zone->zoneid + $this->rules[] ="add ".(5000+$zone->zoneid)." fwd 127.0.0.1,".($forward_port + $zone->zoneid )." tcp from any to any dst-port 80 in via ".$interface->if; + } + + } + } + } + + /** + * for accounting statistics we setup a separate section in our config + * rule number range 30000..65500 + */ + private function generate_accounting_section(){ + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# setup accounting section, first rule is counting all CP traffic "; + $this->rules[] = "# rule 65500 unlocks the traffic already authorized from a CP zone"; + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "add 30000 set 0 count ip from any to any "; + $this->rules[] = "add 65500 pass ip from any to any "; + } + + /** + * generate closure tag, block all traffic coming from captiveportal interfaces + * rule number range 6001..6999 + */ + private function generate_closure(){ + $cpinterfaces = []; + # find all cp interfaces + foreach( $this->config->object()->captiveportal->children() as $cpzonename => $zone ) { + // search interface + $interface = $zone->interface->xpath("//" . $zone->interface); + if (count($interface) > 0) { + $interface = $interface[0]; + $cpinterfaces[$interface->if->__toString()] = 1; + } + + } + + // generate accept rules for every interface not in captive portal + $ruleid = 6001 ; + $this->rules[] = "#========================================================================================================="; + $this->rules[] = "# accept traffic from all interfaces not used by captive portal (5001..5999) "; + $this->rules[] = "#========================================================================================================="; + foreach( $this->config->object()->interfaces->children() as $interface => $content ){ + if ( !isset($cpinterfaces[$content->if->__toString()])){ + $this->rules[] = "add ".($ruleid++)." allow all from any to any via ".$content->if ; + } + } + + + $this->rules[] = "# let the responses from the captive portal web server back out"; + $this->rules[] = "add ".($ruleid++)." pass tcp from any to any out"; + + // block every thing else (not mentioned before) + $this->rules[] = "# block everything else"; + $this->rules[] = "add ".($ruleid)." skipto 65534 all from any to any"; + $this->rules[] = "add 65534 deny all from any to any"; + } + + + + /** + * load ruleset + */ + public function generate($filename){ + /* + * reset rules + */ + $this->rules = [] ; + + /* + * generate new + */ + $this->generate_default_rules(); + $this->generate_this_host_rules(); + $this->generate_zones(); + $this->generate_reflect_rules(); + $this->generate_accounting_section(); + $this->generate_closure(); + + // ruleset array -> text + $ruleset_txt = ""; + $prev_rule = "#"; + foreach($this->rules as $rule){ + if (trim($rule)[0] == '#' && trim($prev_rule)[0] != "#" ) $ruleset_txt .= "\n"; + $ruleset_txt .= $rule."\n"; + $prev_rule = $rule ; + } + + // write to file + file_put_contents($filename,$ruleset_txt); + } + +} \ No newline at end of file diff --git a/src/opnsense/mvc/app/models/Core/Config.php b/src/opnsense/mvc/app/models/Core/Config.php new file mode 100644 index 000000000..6066b7db8 --- /dev/null +++ b/src/opnsense/mvc/app/models/Core/Config.php @@ -0,0 +1,130 @@ +config_file) ) throw new ConfigException('file not found') ; + $xml = file_get_contents($this->config_file); + if (trim($xml) == '') { + throw new ConfigException('empty file') ; + } + + $this->configxml = new \DOMDocument; + $this->configxml->loadXML($xml); + $this->simplexml = simplexml_import_dom($this->configxml); + $this->isValid = true; + + } + + /** + * @throws ConfigException + */ + private function checkvalid(){ + if ( !$this->isValid ) throw new ConfigException('no valid config loaded') ; + } + + /* + * parse configuration and dump to std output (test) + * @param DOMElement $node + * @param string $nodename + * @throws ConfigException + */ + public function dump($node=null,$nodename=""){ + $this->checkvalid(); + // root node + if ($node == null ) $node = $this->configxml; + + $subNodes = $node->childNodes ; + foreach($subNodes as $subNode){ + if ( $subNode->nodeType == XML_TEXT_NODE &&(strlen(trim($subNode->wholeText))>=1)) { + print($nodename.".". $node->tagName." " .$subNode->nodeValue ."\n"); + } + + if ( $subNode->hasChildNodes() ){ + if ( $nodename != "" ) $tmp = $nodename.".".$node->tagName; + elseif ($node != $this->configxml) $tmp = $node->tagName; + else $tmp = ""; + + $this->dump($subNode,$tmp); + } + + } + + } + + /* + * init new config object, try to load current configuration + * (executed via Singleton) + */ + protected function init() { + $this->config_file = \Phalcon\DI\FactoryDefault::getDefault()->get('config')->globals->config_path . "config.xml"; + try { + $this->load(); + } catch (\Exception $e){ + $this->configxml = null ; + } + + } + + /* + * Execute a xpath expression on config.xml + * @param $query + * @return \DOMNodeList + * @throws ConfigException + */ + function xpath($query){ + $this->checkvalid(); + $xpath = new \DOMXPath($this->configxml); + return $xpath->query($query); + } + + /* + * object representation of xml document via simplexml, references the same underlying model + * @return SimpleXML + * @throws ConfigException + */ + function object(){ + $this->checkvalid(); + return $this->simplexml; + } + +} diff --git a/src/opnsense/mvc/app/models/Core/Shell.php b/src/opnsense/mvc/app/models/Core/Shell.php new file mode 100644 index 000000000..7fa57744b --- /dev/null +++ b/src/opnsense/mvc/app/models/Core/Shell.php @@ -0,0 +1,101 @@ +simulate = \Phalcon\DI\FactoryDefault::getDefault()->get('config')->globals->simulate_mode; + $this->debug = \Phalcon\DI\FactoryDefault::getDefault()->get('config')->globals->debug; + + } + + /** + * execute shell command + * @param string $command command to execute + * @param bool $mute + * @param bool $clearsigmask + */ + private function _exec($command, $mute = false, $clearsigmask = false,&$output=null){ + $oarr = array(); + $retval = 0; + + // debug output + if ($this->debug) { + print("Shell->exec : " . $command . " \n"); + } + + // only execute actual command if not in simulation mode + if (!$this->simulate) { + if ($clearsigmask) { + $oldset = array(); + pcntl_sigprocmask(SIG_SETMASK, array(), $oldset); + } + + $garbage = exec("$command 2>&1", $output, $retval); + + if (($retval <> 0) && ($mute === false)) { + //log_error(sprintf(gettext("The command '%1\$s' returned exit code '%2\$d', the output was '%3\$s' "), implode(" ", $output); + // TODO: log + unset($output); + } + + + if ($clearsigmask) { + pcntl_sigprocmask(SIG_SETMASK, $oldset); + } + + unset($oarr); + return $retval; + } + + return null; + } + + /** + * execute command or list of commands + * + * @param string/Array() $command command to execute + * @param bool $mute + * @param bool $clearsigmask + * @param Array() &$output + */ + function exec($command, $mute = false, $clearsigmask = false,&$output=null) + { + if (is_array($command)){ + foreach($command as $comm ){ + $this->_exec($comm,$mute, $clearsigmask ,$output); + } + } + else{ + $this->_exec($command,$mute, $clearsigmask ,$output); + } + + } + +} \ No newline at end of file diff --git a/src/opnsense/mvc/app/models/Core/Singleton.php b/src/opnsense/mvc/app/models/Core/Singleton.php new file mode 100644 index 000000000..d957cc8bb --- /dev/null +++ b/src/opnsense/mvc/app/models/Core/Singleton.php @@ -0,0 +1,68 @@ + + + +You're now flying with Phalcon. Great things are about to happen!
\ No newline at end of file diff --git a/src/opnsense/mvc/script/load_falcon.php b/src/opnsense/mvc/script/load_falcon.php new file mode 100644 index 000000000..4383cdd92 --- /dev/null +++ b/src/opnsense/mvc/script/load_falcon.php @@ -0,0 +1,21 @@ +registerDirs( + array( + $config->application->controllersDir, + $config->application->modelsDir + ) +)->register(); + +$di->set('config',$config); diff --git a/src/opnsense/mvc/script/test.php b/src/opnsense/mvc/script/test.php new file mode 100644 index 000000000..c4b977b9c --- /dev/null +++ b/src/opnsense/mvc/script/test.php @@ -0,0 +1,38 @@ +portal_allow("test","10.211.55.101","00:1C:42:49:B7:B2","Fritsx"); + +$cpc->disconnect("test",array("5489714eba263","gdsajhgadsjhg")); + +//$cpc->reconfigure(); +//$cpc->refresh_allowed_mac(); +//$cpc->refresh_allowed_ips(); + + +//$db = new Captiveportal\DB("test"); +//$db->remove_session("XXX"); +//$db->insert_session(100,1,"10.211.55.101","00:1C:42:49:B7:B2","frits","XXX","aksjdhaskjh", null,null, null,null, null); +// +//$clients = $db->listClients( array("sessionid" => "XXX") ); +// +//foreach($clients as $client ){ +// print($client->pipeno) ; +//} + +//$arp = new \Captiveportal\ARP(); +//$arp->setStatic("172.20.0.1",'00:1c:42:49:b7:b1'); +//$arp->dropStatic("172.20.0.1"); + +//$config = \Core\Core\Config::getInstance(); + +//$config->dump(); +//print_r($config->xpath('//pfsense/interfaces/*') ); + +//$rules= new \Core\Captiveportal\Rules(); + + + + +