action = Request::any('action'); if ($this->action === 'updateConfig') { $this->updateConfig(); } elseif ($this->action === 'addDevices') { $this->addDevices(); } elseif ($this->action === 'deleteConfig') { $this->deleteConfig(); } elseif ($this->action === 'deleteRule') { $this->deleteRule(); } elseif ($this->action === 'editRule') { $this->editRule(); } } /** * Menu etc. has already been generated, now it's time to generate page content. */ protected function doRender() { $show = Request::get("show", "config-table"); if ($show === "config-table") { $this->loadConfigChooser(); } else if ($show === "edit-config") { $configid = Request::get("configid", ""); $dbquery = Database::queryFirst("SELECT configname, configdesc FROM `usb_configs` WHERE configid=:id", array( 'id' => $configid )); $rulesConfigHtml = $this->loadRulesConfig($configid); $daemonConfigHtml = $this->loadDaemonConfig($configid); Render::addTemplate('usb-edit-config', array( 'configid' => $configid, 'configName' => $dbquery['configname'], 'configDesc' => $dbquery['configdesc'], 'rulesConfigHtml' => $rulesConfigHtml, 'daemonConfigHtml' => $daemonConfigHtml )); } else if ($show === "add-devices") { $this->deviceList(); } else if ($show === "add-generic-rule") { $this->addGenericRule(); } else if ($show === "edit-rule") { $ruleid = Request::get("ruleid", 0, "int"); if ($ruleid === 0) { Message::addError('invalid-rule-id'); return; } $configid = Request::get("configid", ""); $this->showEditRule($configid, $ruleid); } } private function editRule() { $ruleid = Request::post('ruleid', 0, 'int'); $configid = Request::post('configid', 0, 'int'); $attributes = json_decode(Request::post('attributes', '', 'string'), true); if ($ruleid != 0) { Database::exec("DELETE FROM `usb_rule_prop` WHERE ruleid=:ruleid", array('ruleid' => $ruleid)); } // Prepare array for the insert. prop- has to be cut and vid:pid = id $a = array(); foreach ($attributes as $att) { // SPECIAL CASE: PID AND VID needs to put together to VID:PID = ID if (substr($att['prop'], 5) === "vid") { if (!array_key_exists('id', $a)) { $a['id'] = ""; } $a['id'] = $att['value'] . ':' . $a['id']; } else if (substr($att['prop'], 5) === "pid") { if (!array_key_exists('id', $a)) { $a['id'] = ""; } $a['id'] = $a['id'] . $att['value']; } else { $a[substr($att['prop'], 5)] = $att['value']; } } foreach ($a as $key => $value) { // TODO: Better in one query? Database::exec("INSERT INTO `usb_rule_prop` (ruleid, prop, value) VALUES (:ruleid, :prop, :val)", array( 'ruleid' => $ruleid, 'prop' => $key, 'val' => $value )); } Message::addSuccess('rule-edited'); Util::redirect('?do=usblockoff&show=edit-config&configid=' . $configid); } private function addDevices() { $configid = Request::any('configid', 0, 'int'); $rules = json_decode(Request::post('rules', '', 'string'), true); foreach ($rules as $rule) { $rid = (int)$rule['id']; if($rid == 0) { // New entry so insert only with new id. $rid = Database::queryFirst("SELECT MAX(ruleid) AS ID FROM `usb_rule_prop`"); $rid = $rid['ID']; if ($rid == null) $rid = 1; else $rid += 1; } else { // Old entry so delete all old ones and insert new ones. Database::exec("DELETE FROM `usb_rule_prop` WHERE ruleid=:ruleid", array('ruleid' => $rid)); } Database::exec("INSERT INTO `usb_rule_prop` (ruleid, prop, value) VALUES (:ruleid, :prop, :val)", array( 'ruleid' => $rid, 'prop' => 'target', 'val' => $rule['target'] )); foreach ($rule['attributes'] as $attribute) { // TODO: Better in one query? Database::exec("INSERT INTO `usb_rule_prop` (ruleid, prop, value) VALUES (:ruleid, :prop, :val)", array( 'ruleid' => $rid, 'prop' => $attribute['prop'], 'val' => $attribute['value'] )); } // TODO: Add id at the end of the config entry. $config = Database::queryFirst("SELECT rulesconfig FROM `usb_configs` WHERE configid=:configid", array( 'configid' => $configid )); $rulesconfig = json_decode($config['rulesconfig'], true); $rulesconfig[] = $rid; Database::exec("UPDATE `usb_configs` SET rulesconfig = :rulesconfig WHERE configid=:configid", array( 'configid' => $configid, 'rulesconfig' => json_encode($rulesconfig) )); //$result['rules'][] = $rid; } Util::redirect('?do=usblockoff&show=edit-config&configid=' . $configid); } private function deviceList() { $configid = Request::get("configid", 0, 'int'); $usbdevices = $this->getUsbDeviceList(); // TODO: Translate Operator Action etc.. $settings = array(); $setting = array(); $setting['title'] = "Action"; $setting['select_list'] = array( array( 'option' => 'allow', 'active' => true, 'value' => 'allow', ), array( 'option' => 'block', 'active' => false, 'value' => 'block', ), array( 'option' => 'reject', 'active' => false, 'value' => 'reject', )); $setting['helptext'] = array('helptext' => Dictionary::translateFile('rule', 'target_helptext')); $setting['property'] = 'action'; $setting['settingHtml'] = Render::parse('server-prop-dropdown', (array)$setting); $settings[] = $setting; $ruleValues = array('id' => true, 'serial' => true, 'name' => true, //'hash' => false, //'parent-hash' => false, 'via-port' => false, 'with-interface' => false); foreach ($ruleValues as $key => $value) { $settings[] = array( 'settingHtml' => Render::parse('server-prop-bool', array( 'title' => Dictionary::translateFile('rule', $key), 'helptext' => array('helptext' => Dictionary::translateFile('rule', $key . "_helptext")), 'property' => $key, 'currentvalue' => $value)), ); } Render::addTemplate('usb-device-list', array( 'list' => array_values($usbdevices), 'settings' => array_values($settings), 'configid' => $configid )); } private function showEditRule($configid, $ruleid) { $attributes = array(); $query = Database::simpleQuery("SELECT * FROM `usb_rule_prop` WHERE ruleid=:ruleid", array( 'ruleid' => $ruleid )); $idList = json_decode($this->getIDList(), true); while ($attribute = $query->fetch(PDO::FETCH_ASSOC)) { $attributesHtml = ''; $attr = array( 'title' => Dictionary::translateFile('rule', $attribute['prop']), 'property' => $attribute['prop'], 'currentvalue' => $attribute['value'], 'helptext' => Dictionary::translateFile('rule', $attribute['prop'] . "_helptext"), ); if ($attribute['prop'] === 'target') { $attr['select_list'] = array( array( 'option' => 'allow', 'active' => ($attribute['value'] === 'allow' ? true : false), 'value' => 'allow', ), array( 'option' => 'block', 'active' => ($attribute['value'] === 'block' ? true : false), 'value' => 'block', ), array( 'option' => 'reject', 'active' => ($attribute['value'] === 'reject' ? true : false), 'value' => 'reject', )); $attributesHtml = Render::parse('server-prop-dropdown', (array)$attr); } else if ($attribute['prop'] === 'id') { $id = explode(':', $attribute['value']); $vid = $id[0]; $pid = $id[1]; // Vendor ID $attr['title'] = Dictionary::translateFile('rule', 'vid'); $attr['select_list'] = array(); $attr['toTextButton'] = true; $attr['property'] = 'vid'; $attr['helptext'] = Dictionary::translateFile('rule', "vid_helptext"); if (array_key_exists($vid, $idList)) { $attr['select_list'][] = array( 'option' => $idList[$vid]['name'], 'active' => true, 'value' => $vid, ); } else { $attr['inputtype'] = 'text'; $attr['value'] = $vid; $attr['select_list'][] = array( 'option' => "unknown", 'active' => true, 'value' => $vid, ); } $attributesHtml = Render::parse('server-prop-dropdown', (array)$attr); $attributes[] = array( 'attributesHtml' => $attributesHtml ); // Product ID $attr['title'] = Dictionary::translateFile('rule', 'pid'); $attr['select_list'] = array(); $attr['property'] = 'pid'; $attr['helptext'] = Dictionary::translateFile('rule', "pid_helptext"); if (array_key_exists($pid, $idList)) { $attr['select_list'][] = array( 'option' => $idList[$vid]['products'][$pid], 'active' => true, 'value' => $pid, ); } else { $attr['select_list'][] = array( 'option' => "unknown", 'active' => true, 'value' => $pid, ); } $attributesHtml = Render::parse('server-prop-dropdown', (array)$attr); } else { $attr['inputtype'] = 'text'; $attributesHtml = Render::parse('server-prop-generic', (array)$attr); } $attributes[] = array( 'attributesHtml' => $attributesHtml ); } Render::addTemplate('usb-edit-rule', array( 'configid' => $configid, 'attributes' => $attributes, 'ruleid' => $ruleid, 'usbJson' => json_encode($idList), )); } /** * Return a sorted list with all vendor / id information as JSON */ function getIDList() { $usblist = array(); // TODO: Online version takes a bit longer to load but is more accurate. (2018 instead of 2015) //$lines = file('http://www.linux-usb.org/usb.ids'); $lines = file('/var/lib/usbutils/usb.ids'); $currentVendor = ''; $br = false; foreach ($lines as $line) { if ($line === "\n" && $br) { // If its the first part (vid - name / pid - name) skip comments else break because the part we needed is finished. break; } else if ($line[0] === '#' || $line === "\n") { continue; } else if (!ctype_space($line[0])) { $br = true; // It's a vendor id. $l = explode(' ', preg_replace('~[\r\n\t]+~', '', $line)); $vendor = array(); $vendor['name'] = $l[1]; $vendor['products'] = array(); $currentVendor = $l[0]; $usblist[$l[0]] = $vendor; } else if (!ctype_space($line[1])) { // It's a product id. $l = explode(' ', preg_replace('~[\r\n\t]+~', '', $line)); $usblist[$currentVendor]['products'][$l[0]] = $l[1]; } else { // It's a interface continue; } } $sortVendorName = []; $sortVendorId = []; foreach ($usblist as $key => $value) { $sortVendorName[] = (string)$value['name']; $sortVendorId[] = (string)$key; $sortProductName = []; $sortProductId = []; $tmp = $value['products']; $keys2 = array_keys($tmp); foreach ($tmp as $k => $v) { $sortProductName[] = $v; $sortProductId[] = $k; } // TODO: SORT_FLAG_CASE array_multisort($sortProductName, SORT_ASC, $sortProductId, SORT_ASC, $tmp, $keys2); $usblist[$key]['products'] = array_combine($keys2, $tmp); } $keys = array_keys($usblist); // TODO: SORT_FLAG_CASE array_multisort($sortVendorName, SORT_ASC, $sortVendorId, SORT_ASC, $usblist, $keys); return json_encode(array_combine($keys, $usblist), JSON_HEX_APOS); } private function addGenericRule($target = 'allow') { $settings = array(); $configid = Request::get("configid", ""); // TODO: Translate Operator Action etc.. $setting = array(); $setting['title'] = "Action"; $setting['select_list'] = array(array( 'option' => 'allow', 'active' => ($target == 'allow' ? true : false), 'value' => 'allow', ), array( 'option' => 'block', 'active' => ($target == 'block' ? true : false), 'value' => 'block', ), array( 'option' => 'reject', 'active' => ($target == 'reject' ? true : false), 'value' => 'reject', )); $setting['helptext'] = array('helptext' => Dictionary::translateFile('rule', 'target_helptext')); $setting['property'] = 'action'; $setting['settingHtml'] = Render::parse('server-prop-dropdown', (array)$setting); $settings[] = $setting; Render::addTemplate('usb-add-generic-rule', array( 'settings' => array_values($settings), 'configid' => $configid )); } protected function loadConfigChooser() { $dbquery = Database::simpleQuery("SELECT configid, configname, configdesc FROM `usb_configs`"); $configs = array(); while ($dbentry = $dbquery->fetch(PDO::FETCH_ASSOC)) { $config['config_id'] = $dbentry['configid']; $config['config_name'] = $dbentry['configname']; $config['config_desc'] = $dbentry['configdesc']; $configs[] = $config; } Render::addTemplate('usb-configuration-table', array('config_list' => array_values($configs))); } protected function deleteConfig() { $configID = Request::any('id', 0, 'int'); if ($configID != 0) { Database::exec("DELETE FROM `usb_configs` WHERE configid=:configid", array('configid' => $configID)); } Message::addSuccess('config-deleted'); Util::redirect('?do=usblockoff'); } protected function deleteRule() { $configid = Request::any('configid', 0, 'int'); $ruleid = Request::any('id', 0, 'int'); if ($ruleid != 0) { Database::exec("DELETE FROM `usb_rule_prop` WHERE ruleid=:ruleid", array('ruleid' => $ruleid)); } Message::addSuccess('rule-deleted'); Util::redirect('?do=usblockoff&show=edit-config&configid=' . $configid); } protected function updateConfig() { $result['saveAsNewConfig'] = Request::post('saveAsNewConfig', false, 'bool'); // Add new settings in usbguard-daemon.conf here: $result['RuleFile'] = Request::post('RuleFile', '', 'string'); $result['ImplicitPolicyTarget'] = Request::post('ImplicitPolicyTarget', '', 'string'); $result['PresentDevicePolicy'] = Request::post('PresentDevicePolicy', '', 'string'); $result['PresentControllerPolicy'] = Request::post('PresentControllerPolicy', '', 'string'); $result['InsertedDevicePolicy'] = Request::post('InsertedDevicePolicy', '', 'string'); $result['RestoreControllerDeviceState'] = Request::post('RestoreControllerDeviceState', '', 'string'); $result['DeviceManagerBackend'] = Request::post('DeviceManagerBackend', '', 'string'); $result['IPCAllowedUsers'] = Request::post('IPCAllowedUsers', '', 'string'); $result['IPCAllowedGroups'] = Request::post('IPCAllowedGroups', '', 'string'); $result['IPCAccessControlFiles'] = Request::post('IPCAccessControlFiles', '', 'string'); $result['DeviceRulesWithPort'] = Request::post('DeviceRulesWithPort', '', 'string'); $result['AuditFilePath'] = Request::post('AuditFilePath', '', 'string'); $result['rules'] = json_decode(Request::post('rules', '', 'string'), true); $id = Request::post('id', 0, 'int'); $configname = Request::post('configName', '', 'string'); $configdesc = Request::post('configDesc', '', 'string'); $dbquery = Database::queryFirst("SELECT * FROM `usb_configs` WHERE configid=:id", array('id' => $id)); // Load daemon.conf from db else load default if ($dbquery !== false) { $daemonConf = explode("\r\n", $dbquery['daemonconfig']); } else { $currentdir = getcwd(); $file = $currentdir . '/modules/usblockoff/inc/default-configs/usbguard-daemon.conf'; $daemonConf = file($file); } $newDaemonConf = array(); foreach ($daemonConf as $line) { $t_line = trim($line, "\r\n"); if ($t_line == '' || $t_line[0] == '#') { $newDaemonConf[] = $line . "\r\n"; continue; } else { $splitstr = explode('=', $line); $splitstr[1] = $result[$splitstr[0]]; $newDaemonConf[] = implode('=', $splitstr) . "\r\n"; } } // INSERT IN DB if ($id == '0' || $result['saveAsNewConfig']) { $dbquery = Database::exec("INSERT INTO `usb_configs` (configname, rulesconfig, daemonconfig, configdesc) VALUES (:configname, :rulesconfig, :daemonconfig, :configdesc)", array('configname' => $configname, 'rulesconfig' => json_encode($result['rules']), 'daemonconfig' => implode($newDaemonConf), 'configdesc' => $configdesc)); } else { $dbquery = Database::exec("UPDATE `usb_configs` SET configname=:configname, rulesconfig=:rulesconfig, daemonconfig=:daemonconfig, configdesc=:configdesc WHERE configid=:configid", array('configid' => $id, 'configname' => $configname, 'rulesconfig' => json_encode($result['rules']), 'daemonconfig' => implode($newDaemonConf), 'configdesc' => $configdesc)); } Message::addSuccess('config-saved'); } private function loadRulesConfig($configid) { $rulesConf = null; if ($configid == 0) { $currentdir = getcwd(); // TODO: No need for that with the new rule db structure. $rulesConf = file_get_contents($currentdir . '/modules/usblockoff/inc/default-configs/rules.conf'); } else { $dbquery = Database::queryFirst("SELECT * FROM `usb_configs` WHERE configid=:id", array('id' => $configid)); $ruleIds = json_decode($dbquery['rulesconfig'], true); } $rulesArray = []; foreach ($ruleIds as $id) { // TODO: Query rule and prepare array for the html file. $dbq = Database::simpleQuery("SELECT * FROM `usb_rule_prop` WHERE ruleid=:id", array('id' => $id)); $rule = []; $rule['id'] = $id; $rule['hasoverload'] = false; $rule['num_overload'] = 0; $rule['attributes'] = array(); $rule['attributes_overload'] = ""; while ($entry = $dbq->fetch(PDO::FETCH_ASSOC)) { if ($entry['prop'] == "target") { $rule['target'] = $entry['value']; } else if ($entry['prop'] == "id") { $idList = json_decode($this->getIDList(), true); $id = explode(":", $entry['value']); if (array_key_exists($id[0], $idList)) { $vendor = $idList[$id[0]]['name']; $attributes = []; $attributes['prop'] = Dictionary::translateFile('rule', 'vendor'); $attributes['value'] = $vendor; $rule['attributes'][] = $attributes; if (array_key_exists($id[1], $idList[$id[0]]['products'])) { $product = $idList[$id[0]]['products'][$id[1]]; $attributes = []; $attributes['prop'] = Dictionary::translateFile('rule', 'product'); $attributes['value'] = $product; $rule['attributes'][] = $attributes; } } else { $attributes = []; $attributes['prop'] = $entry['prop']; $attributes['value'] = $entry['value']; $rule['attributes'][] = $attributes; } } else { $attributes = []; $attributes['prop'] = $entry['prop']; $attributes['value'] = $entry['value']; if(sizeof($rule['attributes']) >= 5) { $rule['hasoverload'] = true; $rule['num_overload'] += 1; $rule['attributes_overload'] .= $attributes['prop'] . ': ' . $attributes['value'] . "
"; } else { $rule['attributes'][] = $attributes; } } } if (!empty($rule['target'])) { $rulesArray[] = $rule; } } if ($configid == "new-default") { $newConfig = true; } else { $newConfig = false; } return Render::parse('usb-rules-config', array( 'rules' => (array)$rulesArray, 'configid' => $configid, 'newConfig' => $newConfig )); } private function loadDaemonConfig($id) { $form = array(); $rulesConf = null; if ($id == 0) { $currentdir = getcwd(); $daemonConf = file($currentdir . '/modules/usblockoff/inc/default-configs/usbguard-daemon.conf'); } else { $dbquery = Database::queryFirst("SELECT * FROM `usb_configs` WHERE configid=:id", array('id' => $id)); $daemonConf = explode("\r\n", $dbquery['daemonconfig']); } $element = array(); $hlptxt = ''; foreach ($daemonConf as $line) { $t_line = trim($line, "\r\n"); if ($t_line == '#' || $t_line == '' || strpos($t_line, '#!!!') !== false) { continue; } elseif ($t_line[0] == '#') { $ttxt = trim($line, "#"); $hlptxt .= $ttxt . '
'; } else { $splitstr = explode('=', $t_line); $element['name'] = $splitstr[0]; $element['value'] = $splitstr[1]; $element['helptext'] = $hlptxt; $form[] = $element; $hlptxt = ''; } } return Render::parse('usb-daemon-config', array( 'list' => array_values($form), )); } /** * AJAX */ protected function doAjax() { User::load(); if (!User::isLoggedIn()) { die('Unauthorized'); } $action = Request::any('action'); // TODO: Removed if not needed anymore. if ($action === '') { //$this->ajaxDeviceList(); } } private function getUsbDeviceList() { $usbdevices = array(); // TODO: Per USB Device 3 querys are executed.. better build a more complex sql query? $uid = 0; $dbquery = Database::simpleQuery("SELECT * FROM `usblockoff_hw`"); while ($entry = $dbquery->fetch(PDO::FETCH_ASSOC)) { $device = array(); // Get all props from the hw table. $dbquery2 = Database::simpleQuery("SELECT * FROM `statistic_hw_prop` WHERE hwid=:hwid", array( 'hwid' => $entry['hwid'] )); while ($prop = $dbquery2->fetch(PDO::FETCH_ASSOC)) { $device[$prop['prop']] = $prop['value']; } // Get all props from the device table. $dbquery3 = Database::simpleQuery("SELECT * FROM `usblockoff_hw_prop` WHERE hwid=:hwid AND serial=:serial", array( 'hwid' => $entry['hwid'], 'serial' => $entry['serial'] )); while ($prop = $dbquery3->fetch(PDO::FETCH_ASSOC)) { $device[$prop['prop']] = $prop['value']; } if (!empty($device['machineuuid'])) { $locationquery = Database::queryFirst("SELECT l.locationname AS 'name', m.clientip AS 'ip' FROM machine AS m JOIN location AS l ON l.locationid=m.locationid WHERE m.machineuuid=:machineuuid", array('machineuuid' => $entry['machineuuid'])); $device['clientip'] = $locationquery['ip']; $device['location'] = $locationquery['name']; } $device['uid'] = ++$uid; $device['id'] = $device['vendorid'] . ":" . $device['productid']; $device['serial'] = $entry['serial']; $device['date'] = date('d.m.Y', $device['lastseen']); $device['time'] = date('G:i', $device['lastseen']); $usbdevices[] = $device; } return $usbdevices; } }