Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

- Encrypt stored auth credentials, parameterize device queries, escape XML/error output
- Sanitize invalid characters from SCCM

## [2.6.0] - 2026-05-26
Expand Down
39 changes: 33 additions & 6 deletions inc/config.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ public function prepareInputForUpdate($input): array
unset($input["sccmdb_password"]);
}

if (isset($input["auth_info"]) && !empty($input["auth_info"])) {
$input["auth_info"] = (new GLPIKey())->encrypt($input["auth_info"]);
} else {
unset($input["auth_info"]);
}

if (array_key_exists('inventory_server_url', $input) && !empty($input['inventory_server_url'])) {
$input['inventory_server_url'] = trim((string) $input['inventory_server_url'], '/ ');
}
Expand All @@ -163,15 +169,17 @@ public function showForm($ID, array $options = []): bool
$this->initForm($ID, $options);

$password = (new GLPIKey())->decrypt($this->fields['sccmdb_password'] ?? '');
$auth_info = (new GLPIKey())->decrypt($this->fields['auth_info'] ?? '');
$url = ($this->fields['inventory_server_url'] ?: __('Example:') . ' ' . $CFG_GLPI['url_base']) . '/front/inventory.php';

TemplateRenderer::getInstance()->display(
'@sccm/config.html.twig',
[
'item' => $this,
'params' => $options,
'password_display' => $password,
'url' => $url,
'item' => $this,
'params' => $options,
'password_display' => $password,
'auth_info_display' => $auth_info,
'url' => $url,
],
);

Expand Down Expand Up @@ -216,7 +224,7 @@ public static function install(Migration $migration): bool
`sccm_collection_name` VARCHAR(255) NULL,
`inventory_server_url` VARCHAR(255) NULL,
`active_sync` tinyint NOT NULL DEFAULT '0',
`verify_ssl_cert` tinyint NOT NULL DEFAULT '0',
`verify_ssl_cert` tinyint NOT NULL DEFAULT '1',
`use_auth_ntlm` tinyint NOT NULL DEFAULT '0',
`unrestricted_auth` tinyint NOT NULL DEFAULT '0',
`use_auth_info` tinyint NOT NULL DEFAULT '0',
Expand Down Expand Up @@ -255,7 +263,7 @@ public static function install(Migration $migration): bool
}

if (!$DB->fieldExists($table, 'verify_ssl_cert')) {
$migration->addField($table, "verify_ssl_cert", "tinyint NOT NULL default '0'");
$migration->addField($table, "verify_ssl_cert", "tinyint NOT NULL default '1'");
$migration->migrationOneTable($table);
}

Expand Down Expand Up @@ -298,6 +306,25 @@ public static function install(Migration $migration): bool
$migration->migrationOneTable($table);
}

// Encrypt auth_info — iterate ALL configs
if (!$DB->fieldExists($table, 'is_auth_info_encrypted')) {
$key = new GLPIKey();
foreach ($DB->request(['FROM' => $table]) as $config_data) {
if (!empty($config_data['auth_info'])) {
$migration->addPostQuery(
$DB->buildUpdate(
$table,
['auth_info' => $key->encrypt($config_data['auth_info'])],
['id' => $config_data['id']],
),
);
}
}

$migration->addField($table, "is_auth_info_encrypted", "tinyint NOT NULL default '1'");
$migration->migrationOneTable($table);
}

if (!$DB->fieldExists($table, 'sccm_collection_name')) {
$migration->addField($table, 'sccm_collection_name', 'string', ['after' => 'sccmdb_password', 'value' => '']);
$migration->migrationOneTable($table);
Expand Down
52 changes: 32 additions & 20 deletions inc/sccm.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ public function getDevices(int $config_id, $where = 0, $limit = 99999999): void

$query = self::getcomputerQuery($collection_name);

$params = [];
if ($where != 0) {
$query .= " WHERE csd.MachineID = '" . $where . "'";
$query .= " WHERE csd.MachineID = ?";
$params[] = $where;
}

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, $params);

$i = 0;
$this->devices = [];
Expand All @@ -114,9 +116,9 @@ public function getDatas(PluginSccmSccmdb $sccm_db, $type, $deviceid, $limit = 9

$query = "SELECT " . implode(',', $fields) . "\n";
$query .= " FROM " . $table . "\n";
$query .= " WHERE MachineID = '" . $deviceid . "'" . "\n";
$query .= " WHERE MachineID = ?" . "\n";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand All @@ -141,9 +143,9 @@ public function getNetwork(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 999999
INNER JOIN v_R_System VrS ON VrS.ResourceID=NeDa.MachineID
INNER JOIN v_GS_NETWORK_ADAPTER net ON net.ResourceID=NeDa.MachineID AND NeDa.ServiceName00=net.ServiceName0
WHERE MACAddress00 is not null
AND NeDa.MachineID = '" . $deviceid . "'";
AND NeDa.MachineID = ?";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand All @@ -163,7 +165,7 @@ public function getSoftware(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
ArPd_64.Publisher0 as \"ArPd-Publisher\"
FROM v_GS_ADD_REMOVE_PROGRAMS_64 ArPd_64
INNER JOIN v_R_System VrS on VrS.ResourceID=ArPd_64.ResourceID
WHERE ArPd_64.ResourceID = {$deviceid}
WHERE ArPd_64.ResourceID = ?
AND (ArPd_64.DisplayName0 is not null and ArPd_64.DisplayName0 <> '')
UNION
SELECT ArPd.DisplayName0 as \"ArPd-DisplayName\",
Expand All @@ -172,10 +174,10 @@ public function getSoftware(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
ArPd.Publisher0 as \"ArPd-Publisher\"
FROM v_GS_ADD_REMOVE_PROGRAMS ArPd
INNER JOIN v_R_System VrS on VrS.ResourceID=ArPd.ResourceID
WHERE ArPd.ResourceID = {$deviceid}
WHERE ArPd.ResourceID = ?
AND (ArPd.DisplayName0 is not null and ArPd.DisplayName0 <> '')";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid, $deviceid]);

$data = [];
$i = 0;
Expand All @@ -202,10 +204,10 @@ public function getMemories(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
GroupID as \"Mem-NumSlots\",
'' as \"Mem-SerialNumber\"
FROM v_GS_PHYSICAL_MEMORY
WHERE ResourceID = '" . $deviceid . "'
WHERE ResourceID = ?
ORDER BY \"Mem-NumSlots\"";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand All @@ -228,10 +230,10 @@ public function getVideos(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 9999999
GroupID as \"Vid-PciSlot\"
FROM v_GS_VIDEO_CONTROLLER
WHERE VideoProcessor0 is not null
AND ResourceID = '" . $deviceid . "'
AND ResourceID = ?
ORDER BY GroupID";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand All @@ -251,9 +253,9 @@ public function getSounds(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 9999999
Manufacturer0 as \"Snd-Manufacturer\",
Name0 as \"Snd-Name\"
FROM v_GS_SOUND_DEVICE
WHERE ResourceID = '" . $deviceid . "'";
WHERE ResourceID = ?";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand Down Expand Up @@ -282,9 +284,9 @@ public function getStorages(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
INNER JOIN v_gs_Disk as gdi on gdi.ResourceID = gld.ResourceID
LEFT JOIN Motherboard_DATA as md on gld.ResourceID = md.MachineID
WHERE gld.GroupID = gdi.GroupID
AND gld.ResourceID = '" . $deviceid . "'";
AND gld.ResourceID = ?";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand All @@ -307,9 +309,9 @@ public function getMedias(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 9999999
SCSITargetID0 as \"Med-SCSITargetId\",
MediaType0 as \"Med-Type\"
FROM v_GS_CDROM
WHERE ResourceID = '" . $deviceid . "'";
WHERE ResourceID = ?";

$result = $sccm_db->exec_query($query);
$result = $sccm_db->exec_query($query, [$deviceid]);

$data = [];
$i = 0;
Expand Down Expand Up @@ -433,6 +435,11 @@ public static function executeCollect($task): int
}

foreach ($PluginSccmSccm->devices as $device_values) {
if (!ctype_digit((string) ($device_values['CSD-MachineID'] ?? ''))) {
Toolbox::logInFile('sccm', sprintf('[Config %s] Invalid device id, skipping: ', $config_id) . ($device_values['CSD-MachineID'] ?? '') . "\n", true);
continue;
}

$PluginSccmSccmxml = new PluginSccmSccmxml($device_values);

$PluginSccmSccmxml->setAccessLog();
Expand Down Expand Up @@ -569,6 +576,11 @@ public static function executePush(CronTask $task): int
$result = $PluginSccmSccmdb->exec_query($query);

while ($tab = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC)) {
if (!ctype_digit((string) ($tab['CSD-MachineID'] ?? ''))) {
Toolbox::logInFile('sccm', sprintf('[Config %s] Invalid device id, skipping: ', $config_id) . ($tab['CSD-MachineID'] ?? '') . "\n", true);
continue;
}

$REP_XML = realpath(GLPI_PLUGIN_DOC_DIR . '/sccm/xml/' . $config_id . '/' . $tab['CSD-MachineID'] . '.ocs');

if ($REP_XML === '0') {
Expand Down Expand Up @@ -599,7 +611,7 @@ public static function executePush(CronTask $task): int
}

if ($config->getField('use_auth_info') == "1") {
curl_setopt($ch, CURLOPT_USERPWD, $config->getField('auth_info'));
curl_setopt($ch, CURLOPT_USERPWD, (new GLPIKey())->decrypt($config->getField('auth_info') ?? ''));
}

$url = ($config->getField('inventory_server_url') ?: $CFG_GLPI['url_base']) . '/front/inventory.php';
Expand Down
6 changes: 3 additions & 3 deletions inc/sccmdb.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ public function disconnect(): void
sqlsrv_close($this->dbconn);
}

public function exec_query($query)
public function exec_query($query, array $params = [])
{
$result = sqlsrv_query($this->dbconn, $query);
$result = sqlsrv_query($this->dbconn, $query, $params);
if ($result == false) {
throw new BadRequestHttpException('Query error: ' . print_r(sqlsrv_errors(), true));
}
Expand All @@ -89,7 +89,7 @@ public function FormatErrors(array $errors): void
$state = "SQLSTATE: " . $error['SQLSTATE'];
$code = "Code: " . $error['code'];
$message = "Message: " . $error['message'];
echo $state . "</br>" . $code . "<br>" . $message . "<br>";
echo htmlspecialchars($state) . "</br>" . htmlspecialchars($code) . "<br>" . htmlspecialchars($message) . "<br>";
Toolbox::logInFile("sccm", $state . PHP_EOL . $code . PHP_EOL . $message . PHP_EOL);
}
}
Expand Down
2 changes: 1 addition & 1 deletion inc/sccmxml.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ public function __construct(public array $data)
<CONTENT>
<VERSIONCLIENT>{$this->agentbuildnumber}</VERSIONCLIENT>
</CONTENT>
<DEVICEID>{$this->device_id}</DEVICEID>
<QUERY>INVENTORY</QUERY>
<PROLOG></PROLOG>
</REQUEST>
XML;
$this->sxml = new SimpleXMLElement($SXML);
$this->sxml->addChild('DEVICEID', $this->device_id);
}

public function setAccessLog(): void
Expand Down
44 changes: 21 additions & 23 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,27 @@
* -------------------------------------------------------------------------
*/

use Rector\Configuration\RectorConfigBuilder;

require_once __DIR__ . '/../../src/Plugin.php';

use Rector\Caching\ValueObject\Storage\FileCacheStorage;
use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersion;
$baseline_file = __DIR__ . '/../../PluginsRector.php';
if (!file_exists($baseline_file)) {
throw new RuntimeException(
sprintf(
'Unable to find "%s". Running rector on a plugin requires a GLPI development checkout that ships PluginsRector.php.',
$baseline_file,
),
);
}

$baseline = require $baseline_file;

/** @var RectorConfigBuilder $config */
$config = $baseline([
__DIR__ . '/front',
__DIR__ . '/inc',
__DIR__ . '/tests',
]);

return RectorConfig::configure()
->withPaths([
__DIR__ . '/front',
__DIR__ . '/inc',
])
->withPhpVersion(PhpVersion::PHP_82)
->withCache(
cacheDirectory: __DIR__ . '/var/rector',
cacheClass: FileCacheStorage::class,
)
->withRootFiles()
->withParallel(timeoutSeconds: 300)
->withImportNames(removeUnusedImports: true)
->withPreparedSets(
deadCode: true,
codeQuality: true,
codingStyle: true,
)
->withPhpSets(php82: true) // apply PHP sets up to PHP 8.2
;
return $config;
36 changes: 35 additions & 1 deletion setup.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
<?php

/**
* -------------------------------------------------------------------------
* SCCM plugin for GLPI
* -------------------------------------------------------------------------
*
* LICENSE
*
* This file is part of SCCM.
*
* SCCM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* SCCM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SCCM. If not, see <http://www.gnu.org/licenses/>.
* -------------------------------------------------------------------------
* @author François Legastelois
* @copyright Copyright (C) 2014-2023 by SCCM plugin team.
* @license GPLv3 https://www.gnu.org/licenses/gpl-3.0.html
* @link https://github.com/pluginsGLPI/sccm
* -------------------------------------------------------------------------
*/

declare(strict_types=1);

/**
* -------------------------------------------------------------------------
* SCCM plugin for GLPI
Expand Down Expand Up @@ -51,7 +82,10 @@ function plugin_init_sccm()
}

// Encryption
$PLUGIN_HOOKS[Hooks::SECURED_FIELDS]['sccm'] = ['glpi_plugin_sccm_configs.sccmdb_password'];
$PLUGIN_HOOKS[Hooks::SECURED_FIELDS]['sccm'] = [
'glpi_plugin_sccm_configs.sccmdb_password',
'glpi_plugin_sccm_configs.auth_info',
];
}

/**
Expand Down
7 changes: 4 additions & 3 deletions templates/config.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@
__('Use specific authentication information', 'sccm')
) }}

{{ fields.textField(
{{ fields.passwordField(
'auth_info',
item.fields['auth_info'],
__('Value for specific authentication', 'sccm')
auth_info_display,
__('Value for specific authentication', 'sccm'),
{'autocomplete': 'off'}
) }}

{{ fields.checkboxField(
Expand Down
Loading