From 4e31f99e098568c5d133125630533dad91d07348 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 30 Jul 2020 17:21:29 +0200 Subject: [serversetup-bwlp-ipxe/statistics] Sanitize strings from clients Some string from the clients might not be well suited for utf-8 representation. Add wrapper functions that clean utf-8 strings, or convert ANSI strings to UTF-8 while removing problematic chars. --- inc/util.inc.php | 33 ++++++++++++++++++++++ .../serversetup-bwlp-ipxe/api.inc.php | 2 +- .../inc/scriptbuilderbase.inc.php | 3 ++ .../inc/scriptbuilderipxe.inc.php | 10 +++---- modules-available/statistics/api.inc.php | 24 +++++++++------- 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/inc/util.inc.php b/inc/util.inc.php index 6be06bf6..83b2d54a 100644 --- a/inc/util.inc.php +++ b/inc/util.inc.php @@ -559,4 +559,37 @@ SADFACE; } } + /** + * Remove any non-utf8 sequences from string. + */ + public static function cleanUtf8(string $string) : string + { + // https://stackoverflow.com/a/1401716/2043481 + $regex = '/ + ( + (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx + | [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx + | [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2 + | [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3 + ){1,100} # ...one or more times + ) + | . # anything else + /x'; + return preg_replace($regex, '$1', $string); + } + + /** + * Remove non-printable < 0x20 chars from ANSI string, then convert to UTF-8 + */ + public static function ansiToUtf8(string $string) : string + { + $regex = '/ + ( + (?: [\x20-\xFF] ){1,100} # ignore lower non-printable range + ) + | . # anything else + /x'; + return iconv('MS-ANSI', 'UTF-8', preg_replace($regex, '$1', $string)); + } + } diff --git a/modules-available/serversetup-bwlp-ipxe/api.inc.php b/modules-available/serversetup-bwlp-ipxe/api.inc.php index dd559efa..dcfa7774 100644 --- a/modules-available/serversetup-bwlp-ipxe/api.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/api.inc.php @@ -7,7 +7,7 @@ } else { $builder = new ScriptBuilderIpxe(); } - $bootEntryId = Request::get('beid', false, 'string'); + $bootEntryId = Util::cleanUtf8(Request::get('beid', false, 'string')); $entryId = Request::get('entryid', false, 'int'); if ($bootEntryId !== false) { $entry = BootEntry::fromDatabaseId($bootEntryId); diff --git a/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderbase.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderbase.inc.php index b9867ed9..7bf9e2a3 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderbase.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderbase.inc.php @@ -59,6 +59,9 @@ abstract class ScriptBuilderBase } $this->hasExtension = $slxExtensions ?? (bool)Request::any('slx-extensions', false, 'int'); $this->uuid = Request::any('uuid', false, 'string'); + if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $this->uuid)) { + $this->uuid = false; + } } /** diff --git a/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php index 1b33a1ac..a6376b0e 100644 --- a/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php +++ b/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php @@ -126,10 +126,8 @@ HERE; { if ($this->hasExtension) { $slxConsoleUpdate = '--update'; - $slxPasswordOnly = '--nouser'; } else { $slxConsoleUpdate = ''; - $slxPasswordOnly = ''; } $serverIp = $this->serverIp; @@ -258,19 +256,18 @@ HERE; if ($special === 'localboot') { // Get preferred localboot method, depending on system model // Check if required arguments are given; if not, spit out according script and chain to self - $uuid = Request::any('uuid', false, 'string'); // Get platform - EFI or PCBIOS $manuf = Request::any('manuf', false, 'string'); $product = Request::any('product', false, 'string'); - if ($uuid === false && $manuf === false && $product === false) { + if ($this->uuid === false && $manuf === false && $product === false) { return $this->redirect('special', 'localboot'); } $BOOT_METHODS = Localboot::BOOT_METHODS[$this->platform]; $localboot = false; $model = false; - if ($uuid !== false && Module::get('statistics') !== false) { + if ($this->uuid !== false && Module::get('statistics') !== false) { // If we have the machine table, we rather try to look up the system model from there, using the UUID - $row = Database::queryFirst('SELECT systemmodel FROM machine WHERE machineuuid = :uuid', ['uuid' => $uuid]); + $row = Database::queryFirst('SELECT systemmodel FROM machine WHERE machineuuid = :uuid', ['uuid' => $this->uuid]); if ($row !== false && !empty($row['systemmodel'])) { $model = $row['systemmodel']; } @@ -284,6 +281,7 @@ HERE; if (!empty($manuf)) { $model .= " ($manuf)"; } + $model = Util::ansiToUtf8($model); } } // Query diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php index f7c0ac32..23b86ef4 100644 --- a/modules-available/statistics/api.inc.php +++ b/modules-available/statistics/api.inc.php @@ -51,8 +51,8 @@ if ($type{0} === '~') { $kvmstate = Request::post('kvmstate', 'UNKNOWN', 'string'); $valid = array('UNKNOWN', 'UNSUPPORTED', 'DISABLED', 'ENABLED'); if (!in_array($kvmstate, $valid)) $kvmstate = 'UNKNOWN'; - $cpumodel = Request::post('cpumodel', '', 'string'); - $systemmodel = Request::post('systemmodel', '', 'string'); + $cpumodel = Util::cleanUtf8(Request::post('cpumodel', '', 'string')); + $systemmodel = Util::cleanUtf8(Request::post('systemmodel', '', 'string')); $id44mb = Request::post('id44mb', 0, 'integer'); if ($id44mb < 0 || $id44mb > 10240000) $id44mb = 0; $badsectors = Request::post('badsectors', 0, 'integer'); @@ -61,7 +61,7 @@ if ($type{0} === '~') { if (!is_string($hostname) || $hostname === $ip) { $hostname = ''; } - $data = Request::post('data', '', 'string'); + $data = Util::cleanUtf8(Request::post('data', '', 'string')); // Prepare insert/update to machine table $new = array( 'uuid' => $uuid, @@ -99,7 +99,7 @@ if ($type{0} === '~') { $moresql .= ' hostname = :hostname,'; } if (($runmode = Request::post('runmode', false, 'string')) !== false) { - $new['currentrunmode'] = $runmode; + $new['currentrunmode'] = Util::cleanUtf8($runmode); $moresql .= ' currentrunmode = :currentrunmode,'; } $new['oldstate'] = $old['state']; @@ -208,7 +208,7 @@ if ($type{0} === '~') { } } if (($runmode = Request::post('runmode', false, 'string')) !== false) { - $params['currentrunmode'] = $runmode; + $params['currentrunmode'] = Util::cleanUtf8($runmode); $strUpdateBoottime .= ' currentrunmode = :currentrunmode, '; } // Figure out what's happening - state changes @@ -227,6 +227,9 @@ if ($type{0} === '~') { if ($sessionLength !== 0 || $old['logintime'] === 0) { // This event is a start of a new session, rather than an update $params['user'] = Request::post('user', null, 'string'); + if (is_string($params['user'])) { + $params['user'] = Util::cleanUtf8($params['user']); + } $res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),' . $strUpdateBoottime . " logintime = UNIX_TIMESTAMP(), currentuser = :user, currentsession = NULL, state = 'OCCUPIED' " @@ -274,7 +277,8 @@ if ($type{0} === '~') { if (!array_key_exists('name', $screen)) continue; // Filter bogus data - $screen['name'] = iconv('UTF-8', 'UTF-8//IGNORE', $screen['name']); + $screen['name'] = Util::cleanUtf8($screen['name']); + $port = Util::cleanUtf8($port); if (empty($screen['name'])) continue; if (array_key_exists($screen['name'], $hwids)) { @@ -305,7 +309,7 @@ if ($type{0} === '~') { . " VALUES (:id, :key, :value) ON DUPLICATE KEY UPDATE value = VALUES(value)", array( 'id' => $machinehwid, 'key' => $key, - 'value' => $value, + 'value' => Util::cleanUtf8($value), )); } } @@ -423,10 +427,10 @@ function writeClientLog($type, $description) // For backwards compat, we require the . prefix if ($type{0} === '.') { if ($type === '.vmchooser-session') { - $user = Request::post('user', 'unknown', 'string'); + $user = Util::cleanUtf8(Request::post('user', 'unknown', 'string')); $loguser = Request::post('loguser', 0, 'int') !== 0; - $sessionName = Request::post('name', 'unknown', 'string'); - $sessionUuid = Request::post('uuid', '', 'string'); + $sessionName = Util::cleanUtf8(Request::post('name', 'unknown', 'string')); + $sessionUuid = Util::cleanUtf8(Request::post('uuid', '', 'string')); $session = strlen($sessionUuid) === 36 ? $sessionUuid : $sessionName; Database::exec("UPDATE machine SET currentuser = :user, currentsession = :session WHERE clientip = :ip", compact('user', 'session', 'ip')); -- cgit v1.2.3-55-g7522