CVE-2023-28855 (published 2023-04-05T18:15Z)

Fields is a GLPI plugin that allows users to add custom fields on GLPI items forms. Prior to versions 1.13.1 and 1.20.4, lack of access control check allows any authenticated user to write data to any fields container, including those to which they have no configured access. Versions 1.13.1 and 1.20.4 contain a patch for this issue.

CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-269
cwe_name: Improper Privilege Management
description: The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.
extended_description: 
            
         
         

Fix information:
repo_url: https://github.com/pluginsGLPI/fields
commit_details: repo_url: https://github.com/pluginsGLPI/fields
author: Cédric Anne
author_date: 2023-04-05 08:54:17+02:00
msg: Merge pull request from GHSA-52vv-hm4x-8584
repository: repo_url: https://github.com/pluginsGLPI/fields
repo_name: pluginsGLPI/fields
description: Additionals fields for GLPI
repo_language: PHP
owner: pluginsGLPI
file_changes: filename: container.form.php
change_type: ModificationType.MODIFY
diff: @@ -53,7 +53,10 @@ if (isset($_POST["add"])) {
     $container->update($_POST);
     Html::back();
 } else if (isset($_POST["update_fields_values"])) {
-    $container->updateFieldsValues($_REQUEST, $_REQUEST['itemtype'], false);
+    $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $_POST['plugin_fields_containers_id']);
+    if ($right > READ) {
+        $container->updateFieldsValues($_REQUEST, $_REQUEST['itemtype'], false);
+    }
     Html::back();
 } else {
     Html::header(

programming_language: PHP
filename: container.class.php
change_type: ModificationType.MODIFY
diff: @@ -967,7 +967,6 @@ HTML;
 
         $itemtypes = [];
         $container = new self();
-        $profile   = new PluginFieldsProfile();
         $found     = $container->find($condition, 'label');
         foreach ($found as $item) {
             //entities restriction
@@ -986,12 +985,8 @@ HTML;
                 continue;
             }
             //profiles restriction
-            $found = $profile->find(['profiles_id' => $_SESSION['glpiactiveprofile']['id'],
-                'plugin_fields_containers_id' => $item['id'],
-                'right' => ['>=', READ]
-            ]);
-            $first_found = array_shift($found);
-            if (!$first_found || $first_found['right'] == null || $first_found['right'] == 0) {
+            $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $item['id']);
+            if ($right < READ) {
                 continue;
             }
 
@@ -1457,16 +1452,10 @@ HTML;
         }
 
         //profiles restriction
-        if (isset($_SESSION['glpiactiveprofile']['id'])) {
-            $profile = new PluginFieldsProfile();
-            if (isset($id)) {
-                $found = $profile->find(['profiles_id' => $_SESSION['glpiactiveprofile']['id'],
-                    'plugin_fields_containers_id' => $id
-                ]);
-                $first_found = array_shift($found);
-                if ($first_found === null || $first_found['right'] == null || $first_found['right'] == 0) {
-                     return false;
-                }
+        if (isset($_SESSION['glpiactiveprofile']['id']) && $id > 0) {
+            $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $id);
+            if ($right < READ) {
+                return false;
             }
         }
 
@@ -1551,9 +1540,16 @@ HTML;
             }
         }
 
-        //need to check if container is usable on this object entity
         $loc_c = new PluginFieldsContainer();
         $loc_c->getFromDB($c_id);
+
+        // check rights on $c_id
+        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $c_id);
+        if (($right > READ) === false) {
+            return;
+        }
+
+        // need to check if container is usable on this object entity
         $entities = [$loc_c->fields['entities_id']];
         if ($loc_c->fields['is_recursive']) {
             $entities = getSonsOf(getTableForItemType('Entity'), $loc_c->fields['entities_id']);

programming_language: PHP
filename: field.class.php
change_type: ModificationType.MODIFY
diff: @@ -722,13 +722,12 @@ class PluginFieldsField extends CommonDBChild
 
     public static function showForTabContainer($c_id, $item)
     {
-        //profile restriction (for reading profile)
-        $profile = new PluginFieldsProfile();
-        $found = $profile->find(['profiles_id' => $_SESSION['glpiactiveprofile']['id'],
-            'plugin_fields_containers_id' => $c_id
-        ]);
-        $first_found = array_shift($found);
-        $canedit = ($first_found['right'] == CREATE);
+        //profile restriction
+        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $c_id);
+        if ($right < READ) {
+            return;
+        }
+        $canedit = $right > READ;
 
         //get fields for this container
         $field_obj = new self();
@@ -794,8 +793,6 @@ class PluginFieldsField extends CommonDBChild
      */
     public static function showForTab($params)
     {
-        Html::requireJs('tinymce');
-
         $item = $params['item'];
 
         $functions = array_column(debug_backtrace(), 'function');
@@ -814,6 +811,7 @@ class PluginFieldsField extends CommonDBChild
         if ($type != 'domtab') {
             $subtype = "";
         }
+
         //find container (if not exist, do nothing)
         if (isset($_REQUEST['c_id'])) {
             $c_id = $_REQUEST['c_id'];
@@ -821,6 +819,13 @@ class PluginFieldsField extends CommonDBChild
             return false;
         }
 
+        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $c_id);
+        if ($right < READ) {
+            return;
+        }
+
+        Html::requireJs('tinymce');
+
         //need to check if container is usable on this object entity
         $loc_c = new PluginFieldsContainer();
         $loc_c->getFromDB($c_id);
@@ -964,10 +969,18 @@ JAVASCRIPT
         }
 
         //get object associated with this fields
-        $tmp = $fields;
-        $first_field = array_shift($tmp);
+        $first_field = reset($fields);
         $container_obj = new PluginFieldsContainer();
-        $container_obj->getFromDB($first_field['plugin_fields_containers_id']);
+        if (!$container_obj->getFromDB($first_field['plugin_fields_containers_id'])) {
+            return false;
+        }
+
+        // check if current profile can edit fields
+        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $container_obj->getID());
+        if ($right < READ) {
+            return;
+        }
+        $canedit = $right > READ;
 
         // Fill status overrides if needed
         if (in_array($item->getType(), PluginFieldsStatusOverride::getStatusItemtypes())) {
@@ -994,29 +1007,9 @@ JAVASCRIPT
             $found_v = array_shift($found_values);
         }
 
-        // find profiles (to check if current profile can edit fields)
-        $fprofile = new PluginFieldsProfile();
-        $found_p = $fprofile->find(
-            [
-                'profiles_id' => $_SESSION['glpiactiveprofile']['id'],
-                'plugin_fields_containers_id' => $first_field['plugin_fields_containers_id'],
-            ]
-        );
-        $first_found_p = array_shift($found_p);
-
         // test status for "CommonITILObject" objects
-        if ($item instanceof CommonITILObject) {
-            $status = $item->fields['status'] ?? null;
-            if (
-                ($status !== null && in_array($status, $item->getClosedStatusArray()))
-                || $first_found_p['right'] != CREATE
-            ) {
-                $canedit = false;
-            }
-        } else {
-            if ($first_found_p['right'] != CREATE) {
-                $canedit = false;
-            }
+        if ($item instanceof CommonITILObject && in_array($item->fields['status'] ?? null, $item->getClosedStatusArray())) {
+            $canedit = false;
         }
 
         //show all fields

programming_language: PHP
filename: profile.class.php
change_type: ModificationType.MODIFY
diff: @@ -196,4 +196,22 @@ class PluginFieldsProfile extends CommonDBRelation
         $fields_profile->deleteByCriteria(['profiles_id' => $profile->fields['id']]);
         return true;
     }
+
+    public static function getRightOnContainer(int $profile_id, int $container_id): int
+    {
+        global $DB;
+
+        $container_profile = $DB->request(
+            [
+                'SELECT'  => ['MAX' => 'right AS right'],
+                'FROM'    => self::getTable(),
+                'WHERE'   => [
+                    'profiles_id' => $profile_id,
+                    'plugin_fields_containers_id' => $container_id,
+                ],
+            ]
+        );
+
+        return (int)$container_profile->current()['right'];
+    }
 }

programming_language: PHP