Skip to content

Commit

Permalink
Rewrite of module, YAML widget/listing, system extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxudo committed Dec 20, 2023
1 parent dcf4858 commit 35a205e
Show file tree
Hide file tree
Showing 22 changed files with 539 additions and 627 deletions.
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
Extensions module
==============

Provides information about third party extensions (kexts).
Provides information about third party extensions (kexts) and system extensions (macOS 10.15+). Special thanks to frogor for helping with the data gathering script.

Database:
Table Schema
-----
* bundle_id - varchar(255) - Bundle ID of the extension
* version - varchar(255) - Version of the extension
* path - TEXT - Directory of extension
* developer - varchar(255) - Codesigner's developer name
* teamid - varchar(255) - Developer's TeamID (useful for System Extension Policy whitelisting in MDM)
* executable - TEXT - Location of executable within extension
* path - text - Directory of extension
* developer - varchar(255) - Code signer's developer name
* teamid - varchar(255) - Developer's Team ID
* executable - text - Location of executable within extension
* boot_uuid - varchar(255) - Boot UUID of the current system extension configuration
* developer_mode - boolean - Is developer mode enabled for system extensions
* extension_policies - text - JSON of the current MDM provided system extension policies
* state - varchar(255) - State of the system extension
* categories - varchar(255) - Category of system extension


Special thanks yo frogor for helping with the data gathering script.
140 changes: 72 additions & 68 deletions extensions_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,91 +8,95 @@
**/
class Extensions_controller extends Module_controller
{

/*** Protect methods with auth! ****/
function __construct()
{
// Store module path
$this->module_path = dirname(__FILE__);
}
/*** Protect methods with auth! ****/
function __construct()
{
// Store module path
$this->module_path = dirname(__FILE__);
}

/**
* Default method
*
**/
function index()
{
echo "You've loaded the extensions module!";
}
/**
* Default method
*
**/
function index()
{
echo "You've loaded the extensions module!";
}

/**
* Get extension bundle ID for widget
/**
* Get data for scroll widget
*
* @return void
* @author tuxudo
**/
public function get_bundle_ids()
{
$obj = new View();
public function get_scroll_widget($column)
{
// Remove non-column name characters
$column = preg_replace("/[^A-Za-z0-9_\-]]/", '', $column);

if (! $this->authorized()) {
$obj->view('json', array('msg' => array('error' => 'Not authenticated')));
return;
}

$extension = new Extensions_model;
$obj->view('json', array('msg' => $extension->get_bundle_ids()));
}

public function get_developer()
{
$obj = new View();
$sql = "SELECT COUNT(CASE WHEN ".$column." <> '' AND ".$column." IS NOT NULL THEN 1 END) AS count, ".$column."
FROM extensions
LEFT JOIN reportdata USING (serial_number)
".get_machine_group_filter()."
AND ".$column." <> '' AND ".$column." IS NOT NULL
GROUP BY ".$column."
ORDER BY count DESC";

if (! $this->authorized()) {
$obj->view('json', array('msg' => array('error' => 'Not authenticated')));
return;
}

$extension = new Extensions_model;
$obj->view('json', array('msg' => $extension->get_developer()));
}

public function get_teamid()
{
$obj = new View();
$queryobj = new Extensions_model;
jsonView($queryobj->query($sql));
}

if (! $this->authorized()) {
$obj->view('json', array('msg' => array('error' => 'Not authenticated')));
return;
/**
* Get data for button widget
*
* @author tuxudo
**/
public function get_button_widget($column)
{
// Remove non-column name characters
$column = preg_replace("/[^A-Za-z0-9_\-]]/", '', $column);

$sql = "SELECT COUNT(CASE WHEN ".$column." = 'activated_enabled' THEN 1 END) AS 'activated_enabled',
COUNT(CASE WHEN ".$column." = 'activated_disabled' THEN 1 END) AS 'activated_disabled',
COUNT(CASE WHEN ".$column." = 'terminated_waiting_to_uninstall_on_reboot' THEN 1 END) AS 'terminated_waiting_to_uninstall_on_reboot',
COUNT(CASE WHEN ".$column." = 'activated_waiting_for_user' THEN 1 END) AS 'activated_waiting_for_user',
COUNT(CASE WHEN ".$column." = 'waiting_for_approval' THEN 1 END) AS 'waiting_for_approval',
COUNT(CASE WHEN ".$column." = 'blocked' THEN 1 END) AS 'blocked'
FROM extensions
LEFT JOIN reportdata USING (serial_number)
WHERE ".get_machine_group_filter('');

$out = [];
$queryobj = new Extensions_model();
foreach($queryobj->query($sql)[0] as $label => $value){
$out[] = ['label' => $label, 'count' => $value];
}
$extension = new Extensions_model;
$obj->view('json', array('msg' => $extension->get_teamid()));
}
/**
* Retrieve data in json format
*
**/

jsonView($out);
}

/**
* Retrieve data in json format
*
**/
public function get_data($serial_number = '')
{
// Remove non-serial number characters
$serial_number = preg_replace("/[^A-Za-z0-9_\-]]/", '', $serial_number);

$obj = new View();

if (! $this->authorized()) {
$obj->view('json', array('msg' => 'Not authorized'));
return;
}


// Remove non-serial number characters
$serial_number = preg_replace("/[^A-Za-z0-9_\-]]/", '', $serial_number);

$sql = "SELECT `name`, `bundle_id`, `version`, `path`, `developer`, `teamid`, `executable`, `boot_uuid`, `developer_mode`, `extension_policies`, `state`, `categories`
FROM `extensions`
WHERE `serial_number` = '$serial_number'";

$queryobj = new Extensions_model();

$sql = "SELECT name, bundle_id, version, path, developer, teamid, executable
FROM extensions
WHERE serial_number = '$serial_number'";

$extensions_tab = $queryobj->query($sql);
$obj->view('json', array('msg' => current(array('msg' => $extensions_tab))));
}

} // END class Extensions_controller
} // End class Extensions_controller
182 changes: 59 additions & 123 deletions extensions_model.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,137 +4,73 @@

class Extensions_model extends \Model {

function __construct($serial='')
{
parent::__construct('id', 'extensions'); //primary key, tablename
$this->rs['id'] = '';
$this->rs['serial_number'] = $serial;
$this->rs['name'] = '';
$this->rs['bundle_id'] = '';
$this->rs['version'] = '';
$this->rs['path'] = '';
$this->rs['developer'] = '';
$this->rs['teamid'] = '';
$this->rs['executable'] = '';
function __construct($serial='')
{
parent::__construct('id', 'extensions'); // Primary key, tablename
$this->rs['id'] = '';
$this->rs['serial_number'] = $serial;
$this->rs['name'] = '';
$this->rs['bundle_id'] = '';
$this->rs['version'] = '';
$this->rs['path'] = '';
$this->rs['developer'] = '';
$this->rs['teamid'] = '';
$this->rs['executable'] = '';
$this->rs['boot_uuid'] = null;
$this->rs['developer_mode'] = null; // Boolean
$this->rs['extension_policies'] = null;
$this->rs['state'] = null;
$this->rs['categories'] = null;

$this->serial_number = $serial;
}

// ------------------------------------------------------------------------
$this->serial_number = $serial;
}


/**
* Get bundle IDs for widget
// ------------------------------------------------------------------------
/**
* Process data sent by postflight
*
* @param string data
* @author tuxudo
**/
public function get_bundle_ids()
{
$out = array();
$sql = "SELECT COUNT(CASE WHEN name <> '' AND bundle_id IS NOT NULL THEN 1 END) AS count, name
FROM extensions
LEFT JOIN reportdata USING (serial_number)
".get_machine_group_filter()."
GROUP BY name
ORDER BY count DESC";

foreach ($this->query($sql) as $obj) {
if ("$obj->count" !== "0") {
$obj->name = $obj->name ? $obj->name : 'Unknown';
$out[] = $obj;
}
}
return $out;
}
function process($plist)
{

public function get_developer()
{
$out = array();
$sql = "SELECT COUNT(CASE WHEN NAME <> '' AND developer IS NOT NULL THEN 1 END) AS count, developer
FROM extensions
LEFT JOIN reportdata USING (serial_number)
".get_machine_group_filter()."
GROUP BY developer
ORDER BY count DESC";

foreach ($this->query($sql) as $obj) {
if ("$obj->count" !== "0") {
$obj->developer = $obj->developer ? $obj->developer : 'Unknown';
$out[] = $obj;
}
if ( ! $plist){
throw new Exception("Error Processing Request: No property list found", 1);
}
return $out;
}

public function get_teamid()
{
$out = array();
$sql = "SELECT COUNT(CASE WHEN NAME <> '' AND teamid IS NOT NULL THEN 1 END) AS count, teamid
FROM extensions
LEFT JOIN reportdata USING (serial_number)
".get_machine_group_filter()."
GROUP BY teamid
ORDER BY count DESC";

foreach ($this->query($sql) as $obj) {
if ("$obj->count" !== "0") {
$obj->teamid = $obj->teamid ? $obj->teamid : 'Unknown';
$out[] = $obj;
}
}
return $out;
}

/**
* Process data sent by postflight
*
* @param string data
* @author tuxudo
**/
function process($plist)
{

if ( ! $plist){
throw new Exception("Error Processing Request: No property list found", 1);
}

// Delete previous set
$this->deleteWhere('serial_number=?', $this->serial_number);
// Delete previous set
$this->deleteWhere('serial_number=?', $this->serial_number);

$parser = new CFPropertyList();
$parser->parse($plist, CFPropertyList::FORMAT_XML);
$plist = $parser->toArray();

$parser = new CFPropertyList();
$parser->parse($plist, CFPropertyList::FORMAT_XML);
$myList = $parser->toArray();

$typeList = array(
'name' => '',
'bundle_id' => '',
'version' => '',
'path' => '',
'developer' => '',
'teamid' => '',
'executable' => ''
);

foreach ($myList as $kext) {
// Check if we have a bundle ID
if( ! array_key_exists("bundle_id", $kext)){
continue;
}
foreach ($plist as $kext) {
// Check if we have a bundle ID
if( ! array_key_exists("bundle_id", $kext)){
continue;
}

// Get extension name
$path_array = explode("/", $kext['path']);
$kext['name'] = str_replace(".kext","",array_pop($path_array));
// Get extension name
$path_array = explode("/", $kext['path']);
$kext['name'] = str_replace([".kext",".systemextension"],["",""],array_pop($path_array));

foreach ($typeList as $key => $value) {
$this->rs[$key] = $value;
if(array_key_exists($key, $kext))
{
$this->rs[$key] = $kext[$key];
}
}

// Save kext
$this->id = '';
$this->save();
}
}
// Add the serial mumber to each entry
$kext['serial_number'] = $this->serial_number;

foreach ($this->rs as $key => $value) {
// If key does not exist in $kext, null it
if ( ! array_key_exists($key, $kext) || $kext[$key] == '' && $kext[$key] != '0') {
$this->rs[$key] = null;
} else {
$this->rs[$key] = $kext[$key];
}
}

// Save kext
$this->id = '';
$this->save();
}
}
}
Loading

0 comments on commit 35a205e

Please sign in to comment.