CVE-2020-11031 (published 2020-09-23T16:15Z)

In GLPI before version 9.5.0, the encryption algorithm used is insecure. The security of the data encrypted relies on the password used, if a user sets a weak/predictable password, an attacker could decrypt data. This is fixed in version 9.5.0 by using a more secure encryption library. The library chosen is sodium.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-327
cwe_name: Use of a Broken or Risky Cryptographic Algorithm
description: The product uses a broken or risky cryptographic algorithm or protocol.
extended_description: 
	   Cryptographic algorithms are the methods by which data is scrambled to prevent observation or influence by unauthorized actors. Insecure cryptography can be exploited to expose sensitive information, modify data in unexpected ways, spoof identities of other users or devices, or other impacts.
           It is very difficult to produce a secure algorithm, and even high-profile algorithms by accomplished cryptographic experts have been broken. Well-known techniques exist to break or weaken various kinds of cryptography. Accordingly, there are a small number of well-understood and heavily studied algorithms that should be used by most products. Using a non-standard or known-insecure algorithm is dangerous because a determined adversary may be able to break the algorithm and compromise whatever data has been protected.
           Since the state of cryptography advances so rapidly, it is common for an algorithm to be considered "unsafe" even if it was once thought to be strong. This can happen when new attacks are discovered, or if computing power increases so much that the cryptographic algorithm no longer provides the amount of protection that was originally thought.
	   For a number of reasons, this weakness is even more challenging to manage with hardware deployment of cryptographic algorithms as opposed to software implementation. First, if a flaw is discovered with hardware-implemented cryptography, the flaw cannot be fixed in most cases without a recall of the product, because hardware is not easily replaceable like software. Second, because the hardware product is expected to work for years, the adversary's computing power will only increase over time.
	 
         

Fix information:
repo_url: https://github.com/glpi-project/glpi
commit_details: repo_url: https://github.com/glpi-project/glpi
author: Johan Cwiklinski
author_date: 2020-05-06 09:50:25+02:00
msg: Merge pull request from GHSA-7xwm-4vjr-jvqh

* Deprecate GLPIKEY usage

CVE-2020-5248

Deprecate GLPIKEY usage, and replace it with key file per instance.
Add a command to generate new key, and update database.
Add plugins hooks to rgister fields or configuration entries to be
handled when updating db.

* Rely on sodium compat for encryption/decryption

New name for key file, handle migration
Add not required sodium extension
Deprecate, fill changelog, drop old keyfile
Key must be generated from dedicated method
repository: repo_url: https://github.com/glpi-project/glpi
repo_name: glpi-project/glpi
description: GLPI is a Free Asset and IT Management Software package, Data center management, ITIL Service Desk, licenses tracking and software auditing.
repo_language: PHP
owner: glpi-project
file_changes: filename: .gitignore
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,6 @@
 /config/config_db*
+/config/glpi.key
+/config/glpicrypt.key
 /config/local_define.php
 /tests/config_db*
 /marketplace/

programming_language: PHP
filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -27,6 +27,7 @@ The present file will list all changes made to the project; according to the
 
 - PHP error_reporting and display_errors configuration directives are no longer overrided by GLPI, unless in debug mode (which forces reporting and display of all errors).
 - `scripts/migrations/racks_plugin.php` has been replaced by `glpi:migration:racks_plugin_to_core` command available using `bin/console`
+- Encryption alogithm improved using libsodium
 
 ### API changes
 
@@ -114,6 +115,8 @@ The present file will list all changes made to the project; according to the
 - `ProjectCost::cloneProject()`
 - `ProjectTeam::cloneProjectTask()`
 - `ProjectTask::cloneProjectTeam()`
+- Usage of `GLPIKEY` constant
+- `Toolbox::encrypt()` and `Toolbox::decrypt()` because they use the old encryption aglogithm
 
 #### Removed
 

programming_language: Markdown
filename: mailcollector.php
change_type: ModificationType.MODIFY
diff: @@ -58,7 +58,7 @@ if (isset($_REQUEST['action'])) {
             if (empty($input["passwd"])) {
                unset($input["passwd"]);
             } else {
-               $input["passwd"] = Toolbox::encrypt(stripslashes($input["passwd"]), GLPIKEY);
+               $input["passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["passwd"]));
             }
          }
 

programming_language: PHP
filename: composer.json
change_type: ModificationType.MODIFY
diff: @@ -36,6 +36,7 @@
         "mexitek/phpcolors": "^0.4.0",
         "michelf/php-markdown": "^1.6",
         "monolog/monolog": "^2.0",
+        "paragonie/sodium_compat": "^1.13",
         "phpmailer/phpmailer": "^6.0",
         "psr/log": "^1.1",
         "psr/simple-cache": "^1.0",
@@ -66,13 +67,15 @@
         "sensiolabs/security-checker": "^6.0"
     },
     "replace": {
+        "paragonie/random_compat": "*",
         "symfony/polyfill-ctype": "*",
         "symfony/polyfill-intl-idn": "*",
         "symfony/polyfill-mbstring": "*",
         "symfony/polyfill-php72": "*"
     },
     "suggest": {
-        "ext-ldap": "Used to provide LDAP authentication and synchronization"
+        "ext-ldap": "Used to provide LDAP authentication and synchronization",
+        "ext-sodium": "Used to provide strong encryption for sensitive data in database"
     },
     "config": {
         "optimize-autoloader": true,

programming_language: Shell
filename: composer.lock
change_type: ModificationType.MODIFY
diff: @@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "267af5215d3b1148d5a2bd588feec8fa",
+    "content-hash": "889748826910604c38cf291aab5b831e",
     "packages": [
         {
             "name": "blueimp/jquery-file-upload",
@@ -59,6 +59,12 @@
                 "upload",
                 "widget"
             ],
+            "funding": [
+                {
+                    "url": "https://github.com/blueimp",
+                    "type": "github"
+                }
+            ],
             "time": "2020-05-05T08:39:01+00:00"
         },
         {
@@ -811,6 +817,12 @@
                 "laminas",
                 "mail"
             ],
+            "funding": [
+                {
+                    "url": "https://funding.communitybridge.org/projects/laminas-project",
+                    "type": "community_bridge"
+                }
+            ],
             "time": "2020-04-21T16:42:19+00:00"
         },
         {
@@ -1179,6 +1191,12 @@
                 "laminas",
                 "zf"
             ],
+            "funding": [
+                {
+                    "url": "https://funding.communitybridge.org/projects/laminas-project",
+                    "type": "community_bridge"
+                }
+            ],
             "time": "2020-04-03T16:01:00+00:00"
         },
         {
@@ -1356,6 +1374,88 @@
             ],
             "time": "2019-12-20T14:22:59+00:00"
         },
+        {
+            "name": "paragonie/sodium_compat",
+            "version": "v1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/sodium_compat.git",
+                "reference": "bbade402cbe84c69b718120911506a3aa2bae653"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bbade402cbe84c69b718120911506a3aa2bae653",
+                "reference": "bbade402cbe84c69b718120911506a3aa2bae653",
+                "shasum": ""
+            },
+            "require": {
+                "paragonie/random_compat": ">=1",
+                "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^3|^4|^5|^6|^7"
+            },
+            "suggest": {
+                "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.",
+                "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security."
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "autoload.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "ISC"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "security@paragonie.com"
+                },
+                {
+                    "name": "Frank Denis",
+                    "email": "jedisct1@pureftpd.org"
+                }
+            ],
+            "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists",
+            "keywords": [
+                "Authentication",
+                "BLAKE2b",
+                "ChaCha20",
+                "ChaCha20-Poly1305",
+                "Chapoly",
+                "Curve25519",
+                "Ed25519",
+                "EdDSA",
+                "Edwards-curve Digital Signature Algorithm",
+                "Elliptic Curve Diffie-Hellman",
+                "Poly1305",
+                "Pure-PHP cryptography",
+                "RFC 7748",
+                "RFC 8032",
+                "Salpoly",
+                "Salsa20",
+                "X25519",
+                "XChaCha20-Poly1305",
+                "XSalsa20-Poly1305",
+                "Xchacha20",
+                "Xsalsa20",
+                "aead",
+                "cryptography",
+                "ecdh",
+                "elliptic curve",
+                "elliptic curve cryptography",
+                "encryption",
+                "libsodium",
+                "php",
+                "public-key cryptography",
+                "secret-key cryptography",
+                "side-channel resistant"
+            ],
+            "time": "2020-03-20T21:48:09+00:00"
+        },
         {
             "name": "phpmailer/phpmailer",
             "version": "v6.1.5",
@@ -1416,6 +1516,20 @@
                 }
             ],
             "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
+            "funding": [
+                {
+                    "url": "https://marcus.bointon.com/donations/",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/Synchro",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/marcusbointon",
+                    "type": "patreon"
+                }
+            ],
             "time": "2020-03-14T14:23:48+00:00"
         },
         {
@@ -1838,6 +1952,12 @@
                 "identifier",
                 "uuid"
             ],
+            "funding": [
+                {
+                    "url": "https://github.com/ramsey",
+                    "type": "github"
+                }
+            ],
             "time": "2020-03-29T20:13:32+00:00"
         },
         {
@@ -2552,6 +2672,20 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
             "time": "2020-03-30T11:41:10+00:00"
         },
         {
@@ -2610,6 +2744,20 @@
                 "portable",
                 "shim"
             ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
             "time": "2020-02-27T09:26:54+00:00"
         },
         {
@@ -4172,6 +4320,20 @@
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
             "time": "2020-02-14T07:42:58+00:00"
         },
         {
@@ -4408,6 +4570,20 @@
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
             "time": "2020-02-07T20:06:44+00:00"
         },
         {
@@ -4467,6 +4643,20 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
             "time": "2020-02-03T10:46:43+00:00"
         }
     ],
@@ -4494,5 +4684,6 @@
     },
     "platform-overrides": {
         "php": "7.2.0"
-    }
+    },
+    "plugin-api-version": "1.1.0"
 }

programming_language: Jupyter Notebook
filename: auth.class.php
change_type: ModificationType.MODIFY
diff: @@ -744,8 +744,7 @@ class Auth extends CommonGLPI {
                   $ds = AuthLDAP::connectToServer($ldap_method["host"],
                                                   $ldap_method["port"],
                                                   $ldap_method["rootdn"],
-                                                  Toolbox::decrypt($ldap_method["rootdn_passwd"],
-                                                                   GLPIKEY),
+                                                  Toolbox::sodiumDecrypt($ldap_method["rootdn_passwd"]),
                                                   $ldap_method["use_tls"],
                                                   $ldap_method["deref_option"]);
 
@@ -1708,4 +1707,4 @@ class Auth extends CommonGLPI {
    static function getIcon() {
       return "fas fa-sign-in-alt";
    }
-}
\ No newline at end of file
+}

programming_language: PHP
filename: authldap.class.php
change_type: ModificationType.MODIFY
diff: @@ -193,8 +193,7 @@ class AuthLDAP extends CommonDBTM {
          if (empty($input["rootdn_passwd"])) {
             unset($input["rootdn_passwd"]);
          } else {
-            $input["rootdn_passwd"] = Toolbox::encrypt(stripslashes($input["rootdn_passwd"]),
-                                                       GLPIKEY);
+            $input["rootdn_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["rootdn_passwd"]));
          }
       }
 
@@ -1457,7 +1456,7 @@ class AuthLDAP extends CommonDBTM {
          $port = $config_ldap->fields['port'];
       }
       $ds = self::connectToServer($host, $port, $config_ldap->fields['rootdn'],
-                                  Toolbox::decrypt($config_ldap->fields['rootdn_passwd'], GLPIKEY),
+                                  Toolbox::sodiumDecrypt($config_ldap->fields['rootdn_passwd']),
                                   $config_ldap->fields['use_tls'],
                                   $config_ldap->fields['deref_option']);
       if ($ds) {
@@ -2613,7 +2612,7 @@ class AuthLDAP extends CommonDBTM {
 
       return $this->connectToServer($this->fields['host'], $this->fields['port'],
                                     $this->fields['rootdn'],
-                                    Toolbox::decrypt($this->fields['rootdn_passwd'], GLPIKEY),
+                                    Toolbox::sodiumDecrypt($this->fields['rootdn_passwd']),
                                     $this->fields['use_tls'],
                                     $this->fields['deref_option']);
    }
@@ -2674,7 +2673,7 @@ class AuthLDAP extends CommonDBTM {
       }
       $ds = self::connectToServer($ldap_method['host'], $ldap_method['port'],
                                   $ldap_method['rootdn'],
-                                  Toolbox::decrypt($ldap_method['rootdn_passwd'], GLPIKEY),
+                                  Toolbox::sodiumDecrypt($ldap_method['rootdn_passwd']),
                                   $ldap_method['use_tls'], $ldap_method['deref_option']);
 
       // Test with login and password of the user if exists
@@ -2691,7 +2690,7 @@ class AuthLDAP extends CommonDBTM {
          foreach (self::getAllReplicateForAMaster($ldap_method['id']) as $replicate) {
             $ds = self::connectToServer($replicate["host"], $replicate["port"],
                                         $ldap_method['rootdn'],
-                                        Toolbox::decrypt($ldap_method['rootdn_passwd'], GLPIKEY),
+                                        Toolbox::sodiumDecrypt($ldap_method['rootdn_passwd']),
                                         $ldap_method['use_tls'], $ldap_method['deref_option']);
 
             // Test with login and password of the user
@@ -3445,7 +3444,7 @@ class AuthLDAP extends CommonDBTM {
 
       if (self::connectToServer($authldap->getField('host'), $authldap->getField('port'),
                                 $authldap->getField('rootdn'),
-                                Toolbox::decrypt($authldap->getField('rootdn_passwd'), GLPIKEY),
+                                Toolbox::sodiumDecrypt($authldap->getField('rootdn_passwd')),
                                 $authldap->getField('use_tls'),
                                 $authldap->getField('deref_option'))) {
          self::showLdapUsers();
@@ -3503,7 +3502,7 @@ class AuthLDAP extends CommonDBTM {
       }
 
       if (isset($input["rootdn_passwd"]) && !empty($input["rootdn_passwd"])) {
-         $input["rootdn_passwd"] = Toolbox::encrypt(stripslashes($input["rootdn_passwd"]), GLPIKEY);
+         $input["rootdn_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["rootdn_passwd"]));
       }
 
       return $input;

programming_language: PHP
filename: config.class.php
change_type: ModificationType.MODIFY
diff: @@ -156,7 +156,7 @@ class Config extends CommonDBTM {
          if (empty($input["smtp_passwd"])) {
             unset($input["smtp_passwd"]);
          } else {
-            $input["smtp_passwd"] = Toolbox::encrypt(stripslashes($input["smtp_passwd"]), GLPIKEY);
+            $input["smtp_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["smtp_passwd"]));
          }
       }
 
@@ -168,8 +168,7 @@ class Config extends CommonDBTM {
          if (empty($input["proxy_passwd"])) {
             unset($input["proxy_passwd"]);
          } else {
-            $input["proxy_passwd"] = Toolbox::encrypt(stripslashes($input["proxy_passwd"]),
-                                                      GLPIKEY);
+            $input["proxy_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["proxy_passwd"]));
          }
       }
 
@@ -2060,6 +2059,8 @@ class Config extends CommonDBTM {
                  'check'   => 'GuzzleHttp\\Client' ],
                [ 'name'    => 'wapmorgan/unified-archive',
                  'check'   => 'wapmorgan\\UnifiedArchive\\UnifiedArchive' ],
+               [ 'name'    => 'paragonie/sodium_compat',
+                 'check'   => 'ParagonIE_Sodium_Compat' ],
       ];
       if (Toolbox::canUseCAS()) {
          $deps[] = [
@@ -2523,6 +2524,9 @@ class Config extends CommonDBTM {
             ],
             'intl' => [
                'required' => true
+            ],
+            'sodium' => [
+               'required' => false
             ]
          ];
       } else {

programming_language: PHP
filename: installcommand.class.php
change_type: ModificationType.MODIFY
diff: @@ -37,6 +37,7 @@ if (!defined('GLPI_ROOT')) {
 }
 
 use DB;
+use GLPIKey;
 use Toolbox;
 
 use Symfony\Component\Console\Exception\RuntimeException;
@@ -75,6 +76,13 @@ class InstallCommand extends AbstractConfigureCommand {
     */
    const ERROR_MISSING_REQUIREMENTS = 8;
 
+   /**
+    * Error code returned when failing to create encryption key file.
+    *
+    * @var integer
+    */
+   const ERROR_CANNOT_CREATE_ENCRYPTION_KEY_FILE = 9;
+
    protected function configure() {
 
       parent::configure();
@@ -210,6 +218,14 @@ class InstallCommand extends AbstractConfigureCommand {
          }
       }
 
+      // Create security key
+      $glpikey = new GLPIKey();
+      if (!$glpikey->keyExists() && !$glpikey->generate()) {
+         $message = __('Security key cannot be generated!');
+         $output->writeln('<error>' . $message . '</error>', OutputInterface::VERBOSITY_QUIET);
+         return self::ERROR_CANNOT_CREATE_ENCRYPTION_KEY_FILE;
+      }
+
       $mysqli = new \mysqli();
       if (intval($db_port) > 0) {
          // Network port

programming_language: PHP
filename: changekeycommand.class.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,110 @@
+<?php
+/**
+ * ---------------------------------------------------------------------
+ * GLPI - Gestionnaire Libre de Parc Informatique
+ * Copyright (C) 2015-2018 Teclib' and contributors.
+ *
+ * http://glpi-project.org
+ *
+ * based on GLPI - Gestionnaire Libre de Parc Informatique
+ * Copyright (C) 2003-2014 by the INDEPNET Development Team.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * LICENSE
+ *
+ * This file is part of GLPI.
+ *
+ * GLPI 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GLPI 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 GLPI. If not, see <http://www.gnu.org/licenses/>.
+ * ---------------------------------------------------------------------
+ */
+
+namespace Glpi\Console\Security;
+
+if (!defined('GLPI_ROOT')) {
+   die("Sorry. You can't access this file directly");
+}
+
+use Glpi\Console\AbstractCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+use GLPIKey;
+
+class ChangekeyCommand extends AbstractCommand {
+   /**
+    * Error code returned when unable to renew key.
+    *
+    * @var integer
+    */
+   const ERROR_UNABLE_TO_RENEW_KEY = 1;
+
+   protected function configure() {
+      parent::configure();
+
+      $this->setName('glpi:security:change_key');
+      $this->setDescription(__('Change password storage key and update values in database.'));
+   }
+
+   protected function execute(InputInterface $input, OutputInterface $output) {
+      $glpikey = new GLPIKey();
+
+      $fields = $glpikey->getFields();
+      $configs = $glpikey->getConfigs();
+      $conf_count = 0;
+      foreach ($configs as $config) {
+         $conf_count += count($config);
+      }
+
+      $output->writeln(
+         sprintf(
+            '<info>' . __('Found %1$s field(s) and %2$s configuration entries requiring migration.') . '</info>',
+            count($fields),
+            $conf_count
+         )
+      );
+
+      if (!$input->getOption('no-interaction')) {
+         // Ask for confirmation (unless --no-interaction)
+         $question_helper = $this->getHelper('question');
+         $run = $question_helper->ask(
+            $input,
+            $output,
+            new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true)
+         );
+         if (!$run) {
+            $output->writeln(
+               '<comment>' . __('Aborted.') . '</comment>',
+               OutputInterface::VERBOSITY_VERBOSE
+            );
+            return 0;
+         }
+      }
+
+      $created = $glpikey->generate();
+      if (!$created) {
+         $output->writeln(
+            '<error>' . __('Unable to change security key!') . '</error>',
+            OutputInterface::VERBOSITY_QUIET
+         );
+         return self::ERROR_UNABLE_TO_RENEW_KEY;
+      }
+
+      $this->output->write(PHP_EOL);
+
+      $output->writeln('<info>' . __('New security key generated; database updated.') . '</info>');
+
+      return 0; // Success
+   }
+}

programming_language: PHP
filename: grid.class.php
change_type: ModificationType.MODIFY
diff: @@ -462,7 +462,7 @@ JAVASCRIPT;
    }
 
    static function getToken(string $dasboard = "", int $entities_id = 0, int $is_recursive = 0): string {
-      $seed         = $dasboard.$entities_id.$is_recursive.GLPIKEY;
+      $seed         = $dasboard.$entities_id.$is_recursive.Telemetry::getInstanceUuid();
       $uuid         = Uuid::uuid5(Uuid::NAMESPACE_OID, $seed);
       $token        = $uuid->toString();
 
@@ -1366,4 +1366,4 @@ HTML;
 
       return \Dropdown::showFromArray($name, $options_dashboards, $params);
    }
-}
\ No newline at end of file
+}

programming_language: PHP
filename: glpikey.class.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,271 @@
+<?php
+/**
+ * ---------------------------------------------------------------------
+ * GLPI - Gestionnaire Libre de Parc Informatique
+ * Copyright (C) 2015-2018 Teclib' and contributors.
+ *
+ * http://glpi-project.org
+ *
+ * based on GLPI - Gestionnaire Libre de Parc Informatique
+ * Copyright (C) 2003-2014 by the INDEPNET Development Team.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * LICENSE
+ *
+ * This file is part of GLPI.
+ *
+ * GLPI 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GLPI 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 GLPI. If not, see <http://www.gnu.org/licenses/>.
+ * ---------------------------------------------------------------------
+ */
+
+if (!defined('GLPI_ROOT')) {
+   die("Sorry. You can't access this file directly");
+}
+
+/**
+ *  GLPI security key
+**/
+class GLPIKey {
+   /**
+    * Key file path.
+    *
+    * @var string
+    */
+   private $keyfile;
+
+   /**
+    * Legacy key file path.
+    *
+    * @var string
+    */
+   private $legacykeyfile;
+
+   /**
+    * List of crypted DB fields.
+    *
+    * @var array
+    */
+   protected $fields = [
+      'glpi_mailcollectors.passwd',
+      'glpi_authldaps.rootdn_passwd'
+   ];
+
+   /**
+    * List of crypted configuration values.
+    * Each key corresponds to a configuration context, and contains list of configs names.
+    *
+    * @var array
+    */
+   protected $configs = [
+      'core'   => [
+         'smtp_passwd',
+         'proxy_passwd'
+      ]
+   ];
+
+   public function __construct() {
+      $this->keyfile = GLPI_CONFIG_DIR . '/glpicrypt.key';
+      $this->legacykeyfile = GLPI_CONFIG_DIR . '/glpi.key';
+   }
+
+   /**
+    * Check if GLPI security key used for decryptable passwords exists
+    *
+    * @return string
+    */
+   public function keyExists() {
+      return file_exists($this->keyfile) && !empty($this->get());
+   }
+
+   /**
+    * Get GLPI security key used for decryptable passwords
+    *
+    * @throw \RuntimeException if key file is missing
+    *
+    * @return string
+    */
+   public function get() {
+      if (!file_exists($this->keyfile)) {
+         throw new \RuntimeException('You must create a security key, see glpi:security:change_key command.');
+      }
+      //load key from existing config file
+      $key = file_get_contents($this->keyfile);
+      return $key;
+   }
+
+   /**
+    * Get GLPI security legacy key that was used for decryptable passwords
+    *
+    * @return string
+    *
+    * @deprecated 9.5.0
+    */
+   public function getLegacyKey() {
+      Toolbox::deprecated();
+
+      if (!file_exists($this->legacykeyfile)) {
+         return GLPIKEY;
+      }
+      //load key from existing config file
+      $key = file_get_contents($this->legacykeyfile);
+      return $key;
+   }
+
+   /**
+    * Generate GLPI security key used for decryptable passwords
+    * and update values in DB if necessary.
+    * @return boolean
+    */
+   public function generate() {
+      global $DB;
+
+      $sodium_key = null;
+      $old_key = false;
+
+      try {
+         $sodium_key = $this->get();
+      } catch (\RuntimeException $e) {
+         $sodium_key = null;
+         $old_key = @$this->getLegacyKey();
+      }
+
+      $key = sodium_crypto_aead_chacha20poly1305_ietf_keygen();
+      $success = (bool)file_put_contents($this->keyfile, $key);
+      if (!$success) {
+         return false;
+      }
+
+      if ($DB instanceof DBmysql) {
+         return $this->migrateFieldsInDb($sodium_key, $old_key)
+            && $this->migrateConfigsInDb($sodium_key, $old_key);
+      }
+
+      return true;
+   }
+
+   /**
+    * Get fields
+    *
+    * @return array
+    */
+   public function getFields() :array {
+      global $PLUGIN_HOOKS;
+
+      $fields = $this->fields;
+      if (isset($PLUGIN_HOOKS['secured_fields'])) {
+         foreach ($PLUGIN_HOOKS['secured_fields'] as $plugfields) {
+            $fields = array_merge($fields, $plugfields);
+         }
+      }
+
+      return $fields;
+   }
+
+   /**
+    * Get configs
+    *
+    * @return array
+    */
+   public function getConfigs() :array {
+      global $PLUGIN_HOOKS;
+
+      $configs = $this->configs;
+
+      if (isset($PLUGIN_HOOKS['secured_configs'])) {
+         foreach ($PLUGIN_HOOKS['secured_configs'] as $plugin => $plugconfigs) {
+            $configs['plugin:' . $plugin] = $plugconfigs;
+         }
+      }
+
+      return $configs;
+   }
+
+   /**
+    * Migrate fields in database
+    *
+    * @param string       $sodium_key Current key
+    * @param string|false $old_key     Old key, if any
+    *
+    * @return void
+    */
+   protected function migrateFieldsInDb($sodium_key, $old_key = false) {
+      global $DB;
+
+      $success = true;
+
+      foreach ($this->getFields() as $field) {
+         list($table, $column) = explode('.', $field);
+
+         $iterator = $DB->request([
+            'SELECT' => ['id', $column],
+            'FROM'   => $table
+         ]);
+
+         while ($success && $row = $iterator->next()) {
+            if ($old_key === false) {
+               $pass = Toolbox::sodiumEncrypt(Toolbox::sodiumDecrypt($row[$column], $sodium_key));
+            } else {
+               $pass = Toolbox::sodiumEncrypt(Toolbox::decrypt($row[$column], $old_key));
+            }
+            $success = $DB->update(
+               $table,
+               [$field  => $pass],
+               ['id'    => $row['id']]
+            );
+         }
+      }
+
+      return $success;
+   }
+
+   /**
+    * Migrate configurations in database
+    *
+    * @param string       $sodium_key Current key
+    * @param string|false $old_key    Old key, if any
+    *
+    * @return boolean
+    */
+   protected function migrateConfigsInDb($sodium_key, $old_key = false) {
+      global $DB;
+
+      $success = true;
+
+      foreach ($this->getConfigs() as $context => $names) {
+         $iterator = $DB->request([
+            'FROM'   => Config::getTable(),
+            'WHERE'  => [
+               'context'   => $context,
+               'name'      => $names
+            ]
+         ]);
+
+         while ($success && $row = $iterator->next()) {
+            if ($old_key === false) {
+               $pass = Toolbox::sodiumEncrypt(Toolbox::sodiumDecrypt($row['value'], $sodium_key));
+            } else {
+               $pass = Toolbox::sodiumEncrypt(Toolbox::decrypt($row['value'], $old_key));
+            }
+            $success = $DB->update(
+               Config::getTable(),
+               ['value' => $pass],
+               ['id'    => $row['id']]
+            );
+         }
+      }
+
+      return $success;
+   }
+}

programming_language: PHP
filename: glpimailer.class.php
change_type: ModificationType.MODIFY
diff: @@ -65,7 +65,7 @@ class GLPIMailer extends PHPMailer {
          if ($CFG_GLPI['smtp_username'] != '') {
             $this->SMTPAuth = true;
             $this->Username = $CFG_GLPI['smtp_username'];
-            $this->Password = Toolbox::decrypt($CFG_GLPI['smtp_passwd'], GLPIKEY);
+            $this->Password = Toolbox::sodiumDecrypt($CFG_GLPI['smtp_passwd']);
          }
 
          if ($CFG_GLPI['smtp_mode'] == MAIL_SMTPSSL) {

programming_language: PHP
filename: mailcollector.class.php
change_type: ModificationType.MODIFY
diff: @@ -138,7 +138,7 @@ class MailCollector  extends CommonDBTM {
          if (empty($input["passwd"])) {
             unset($input["passwd"]);
          } else {
-            $input["passwd"] = Toolbox::encrypt(stripslashes($input["passwd"]), GLPIKEY);
+            $input["passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["passwd"]));
          }
       }
 
@@ -1249,7 +1249,7 @@ class MailCollector  extends CommonDBTM {
       $params = [
          'host'      => $config['address'],
          'user'      => $this->fields['login'],
-         'password'  => Toolbox::decrypt($this->fields['passwd'], GLPIKEY),
+         'password'  => Toolbox::sodiumDecrypt($this->fields['passwd']),
          'port'      => $config['port']
       ];
 
@@ -1291,7 +1291,7 @@ class MailCollector  extends CommonDBTM {
          $this->marubox = imap_open(
             $this->fields['host'],
             $this->fields['login'],
-            Toolbox::decrypt($this->fields['passwd'], GLPIKEY),
+            Toolbox::sodiumDecrypt($this->fields['passwd']),
             CL_EXPUNGE,
             1
          );
@@ -1304,7 +1304,7 @@ class MailCollector  extends CommonDBTM {
             $this->marubox = imap_open(
                $this->fields['host'],
                $this->fields['login'],
-               Toolbox::decrypt($this->fields['passwd'], GLPIKEY),
+               Toolbox::sodiumDecrypt($this->fields['passwd']),
                CL_EXPUNGE,
                1,
                $option

programming_language: PHP
filename: plugins.class.php
change_type: ModificationType.MODIFY
diff: @@ -64,7 +64,7 @@ class Plugins {
       // add proxy string if configured in glpi
       if (!empty($CFG_GLPI["proxy_name"])) {
          $proxy_creds      = !empty($CFG_GLPI["proxy_user"])
-            ? $CFG_GLPI["proxy_user"].":".Toolbox::decrypt($CFG_GLPI["proxy_passwd"], GLPIKEY)."@"
+            ? $CFG_GLPI["proxy_user"].":".Toolbox::sodiumDecrypt($CFG_GLPI["proxy_passwd"])."@"
             : "";
          $proxy_string     = "http://{$proxy_creds}".$CFG_GLPI['proxy_name'].":".$CFG_GLPI['proxy_port'];
          $options['proxy'] = $proxy_string;

programming_language: PHP
filename: rssfeed.class.php
change_type: ModificationType.MODIFY
diff: @@ -876,8 +876,7 @@ class RSSFeed extends CommonDBVisible {
          if (!empty($CFG_GLPI["proxy_user"])) {
             $prx_opt[CURLOPT_HTTPAUTH]     = CURLAUTH_ANYSAFE;
             $prx_opt[CURLOPT_PROXYUSERPWD] = $CFG_GLPI["proxy_user"].":".
-                                             Toolbox::decrypt($CFG_GLPI["proxy_passwd"],
-                                                              GLPIKEY);
+                                             Toolbox::sodiumDecrypt($CFG_GLPI["proxy_passwd"]);
          }
          $feed->set_curl_options($prx_opt);
       }

programming_language: PHP
filename: requirementsmanager.class.php
change_type: ModificationType.MODIFY
diff: @@ -89,6 +89,7 @@ class RequirementsManager {
       $requirements[] = new Extension('exif', true);
       $requirements[] = new Extension('zip', true);
       $requirements[] = new Extension('bz2', true);
+      $requirements[] = new Extension('sodium', true);
 
       if ($db instanceof \DBmysql) {
          $requirements[] = new DbEngine($db);

programming_language: PHP
filename: toolbox.class.php
change_type: ModificationType.MODIFY
diff: @@ -241,7 +241,13 @@ class Toolbox {
     *
     * @return string  encrypted string
    **/
-   static function encrypt($string, $key) {
+   static function encrypt($string, $key = null) {
+      self::deprecated('Use sodiumEncrypt');
+
+      if ($key === null) {
+         $glpikey = new GLPIKey();
+         $key = $glpikey->getLegacyKey();
+      }
 
       $result = '';
       for ($i=0; $i<strlen($string); $i++) {
@@ -253,6 +259,40 @@ class Toolbox {
       return base64_encode($result);
    }
 
+   public static function sodiumEncrypt($content, $key = null) {
+      if ($key === null) {
+         $key = self::getGlpiSecKey();
+      }
+
+      $nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES); // NONCE = Number to be used ONCE, for each message
+      $encrypted = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
+         $content,
+         $nonce,
+         $nonce,
+         $key
+      );
+      return base64_encode($nonce . $encrypted);
+   }
+
+   public static function sodiumDecrypt($content, $key = null) {
+      if ($key === null) {
+         $key = self::getGlpiSecKey();
+      }
+
+      $content = base64_decode($content);
+      $nonce = mb_substr($content, 0, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES, '8bit');
+      $ciphertext = mb_substr($content, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit');
+      $plaintext = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
+         $ciphertext,
+         $nonce,
+         $nonce,
+         $key
+      );
+      if (!is_string($plaintext)) {
+         throw new \RuntimeException('Unable to decrypt content');
+      }
+      return $plaintext;
+   }
 
    /**
     * Decrypt a string
@@ -262,7 +302,13 @@ class Toolbox {
     *
     * @return string  decrypted string
    **/
-   static function decrypt($string, $key) {
+   static function decrypt($string, $key = null) {
+      self::deprecated('Use sodiumDecrypt');
+
+      if ($key === null) {
+         $glpikey = new GLPIKey();
+         $key = $glpikey->getLegacyKey();
+      }
 
       $result = '';
       $string = base64_decode($string);
@@ -277,6 +323,18 @@ class Toolbox {
       return Toolbox::unclean_cross_side_scripting_deep($result);
    }
 
+   /**
+    * Get GLPI security key used for decryptable passwords from file
+    *
+    * @throw \RuntimeException if key file is missing
+    *
+    * @return string
+    */
+   public static function getGlpiSecKey() {
+      $glpikey = new GLPIKey();
+      return $glpikey->get();
+   }
+
 
    /**
     * Prevent from XSS
@@ -1614,7 +1672,7 @@ class Toolbox {
          if (!empty($CFG_GLPI["proxy_user"])) {
             $opts += [
                CURLOPT_PROXYAUTH    => CURLAUTH_BASIC,
-               CURLOPT_PROXYUSERPWD => $CFG_GLPI["proxy_user"] . ":" . self::decrypt($CFG_GLPI["proxy_passwd"], GLPIKEY),
+               CURLOPT_PROXYUSERPWD => $CFG_GLPI["proxy_user"] . ":" . self::sodiumDecrypt($CFG_GLPI["proxy_passwd"]),
             ];
          }
 

programming_language: PHP
filename: update.class.php
change_type: ModificationType.MODIFY
diff: @@ -163,7 +163,8 @@ class Update extends CommonGLPI {
       $updir = __DIR__ . "/../install/";
 
       if (isCommandLine() && version_compare($current_version, '0.72.3', 'lt')) {
-         die('Upgrade from command line is not supported before 0.72.3!');
+         echo 'Upgrade from command line is not supported before 0.72.3!';
+         die(1);
       }
 
       // Update process desactivate all plugins
@@ -511,6 +512,12 @@ class Update extends CommonGLPI {
       $crontask_telemetry->getFromDBbyName("Telemetry", "telemetry");
       $crontask_telemetry->resetDate();
       $crontask_telemetry->resetState();
+
+      //generate security key if missing, and update db
+      $glpikey = new GLPIKey();
+      if (!$glpikey->keyExists() && !$glpikey->generate()) {
+         $this->migration->displayWarning(__('Unable to create security key file!'), true);
+      }
    }
 
    /**

programming_language: PHP
filename: install.php
change_type: ModificationType.MODIFY
diff: @@ -368,6 +368,19 @@ function step4 ($databasename, $newdatabasename) {
       Html::closeForm();
    }
 
+   //create security key
+   $glpikey = new GLPIKey();
+   $secured = $glpikey->keyExists();
+   if (!$secured) {
+      $secured = $glpikey->generate();
+   }
+
+   if (!$secured) {
+      echo "<p><strong>".__('Security key cannot be generated!')."</strong></p>";
+      prev_form($host, $user, $password);
+      return;
+   }
+
    //Check if the port is in url
    $hostport = explode(":", $host);
    if (count($hostport) < 2) {
@@ -521,6 +534,7 @@ function step8() {
    Session::destroy(); // Remove session data (debug mode for instance) set by web installation
 
    echo "<h2>".__('The installation is finished')."</h2>";
+
    echo "<p>".__('Default logins / passwords are:')."</p>";
    echo "<p><ul><li> ".__('glpi/glpi for the administrator account')."</li>";
    echo "<li>".__('tech/tech for the technician account')."</li>";

programming_language: PHP
filename: update_0782_080.php
change_type: ModificationType.MODIFY
diff: @@ -951,7 +951,7 @@ function update0782to080() {
             $value = $DB->result($result, 0, 0);
             if (!empty($value)) {
                $query = "UPDATE `glpi_configs`
-                         SET `proxy_passwd` = '".addslashes(Toolbox::encrypt($value, GLPIKEY))."'
+                         SET `proxy_passwd` = '".addslashes(Toolbox::encrypt($value))."'
                          WHERE `id` = '1' ";
                $DB->queryOrDie($query, "0.80 update proxy_passwd in glpi_configs");
             }
@@ -973,7 +973,7 @@ function update0782to080() {
             $value = $DB->result($result, 0, 0);
             if (!empty($value)) {
                $query = "UPDATE `glpi_configs`
-                         SET `smtp_passwd` = '".addslashes(Toolbox::encrypt($value, GLPIKEY))."'
+                         SET `smtp_passwd` = '".addslashes(Toolbox::encrypt($value))."'
                          WHERE `id` = '1' ";
                $DB->queryOrDie($query, "0.80 update smtp_passwd in glpi_configs");
             }
@@ -996,8 +996,7 @@ function update0782to080() {
             while ($data = $DB->fetchAssoc($result)) {
                if (!empty($data['rootdn_password'])) {
                   $query = "UPDATE `glpi_authldaps`
-                            SET `rootdn_passwd` = '".addslashes(Toolbox::encrypt($data['rootdn_password'],
-                                                                                 GLPIKEY))."'
+                            SET `rootdn_passwd` = '".addslashes(Toolbox::encrypt($data['rootdn_password']))."'
                             WHERE `id` = '".$data['id']."' ";
                   $DB->queryOrDie($query, "0.80 update rootdn_passwd in glpi_authldaps");
                }
@@ -1123,8 +1122,7 @@ function update0782to080() {
             while ($data = $DB->fetchAssoc($result)) {
                if (!empty($data['password'])) {
                   $query = "UPDATE `glpi_mailcollectors`
-                            SET `passwd` = '".addslashes(Toolbox::encrypt($data['password'],
-                                                                          GLPIKEY))."'
+                            SET `passwd` = '".addslashes(Toolbox::encrypt($data['password']))."'
                             WHERE `id`= '".$data['id']."' ";
                   $DB->queryOrDie($query, "0.80 update passwd in glpi_mailcollectors");
                }

programming_language: PHP
filename: update_94_95.php
change_type: ModificationType.MODIFY
diff: @@ -1784,6 +1784,24 @@ HTML
       );
    }
 
+   /** Appliances & webapps */
+   require __DIR__ . '/update_94_95/appliances.php';
+   /** /Appliances & webapps */
+
+   // ************ Keep it at the end **************
+   foreach ($ADDTODISPLAYPREF as $type => $tab) {
+      $rank = 1;
+      foreach ($tab as $newval) {
+         $DB->updateOrInsert("glpi_displaypreferences", [
+            'rank'      => $rank++
+         ], [
+            'users_id'  => "0",
+            'itemtype'  => $type,
+            'num'       => $newval,
+         ]);
+      }
+   }
+
    // Add new field states in contract
    if (!$DB->fieldExists('glpi_states', 'is_visible_contract')) {
       $migration->addField('glpi_states', 'is_visible_contract', 'bool', [

programming_language: PHP
filename: status.php
change_type: ModificationType.MODIFY
diff: @@ -104,8 +104,7 @@ if (($ok_master || $ok_slave )
       foreach ($ldap_methods as $method) {
          echo " ".$method['name'];
          if (AuthLDAP::tryToConnectToServer($method, $method["rootdn"],
-                                            Toolbox::decrypt($method["rootdn_passwd"],
-                                            GLPIKEY))) {
+                                            Toolbox::sodiumDecrypt($method["rootdn_passwd"]))) {
             echo "_OK";
          } else {
             echo "_PROBLEM";

programming_language: PHP
filename: AuthLdap.php
change_type: ModificationType.MODIFY
diff: @@ -154,28 +154,6 @@ class AuthLDAP extends DbTestCase {
       $result = $ldap->prepareInputForUpdate($input);
       $this->array($result)->notHasKey('rootdn_passwd');
 
-      //rootdn_passwd is set with a value (a password, not encrypted)
-      $password = 'toto';
-      $input    = ['name' => 'ldap', 'rootdn_passwd' => $password];
-      $result   = $ldap->prepareInputForUpdate($input);
-
-      //Expected value to be encrypted using GLPIKEY key
-      $expected = \Toolbox::encrypt(stripslashes($password), GLPIKEY);
-      $this->string($result['rootdn_passwd'])->isIdenticalTo($expected);
-
-      $password = 'tot\'o';
-      $input    = ['name' => 'ldap', 'rootdn_passwd' => $password];
-      $result   = $ldap->prepareInputForUpdate($input);
-
-      //Expected value to be encrypted using GLPIKEY key
-      $expected = \Toolbox::encrypt(stripslashes($password), GLPIKEY);
-      $this->string($result['rootdn_passwd'])->isIdenticalTo($expected);
-
-      $input['_blank_passwd'] = 1;
-      $result   = $ldap->prepareInputForUpdate($input);
-      //rootdn_passwd is set but empty
-      $this->string($result['rootdn_passwd'])->isEmpty();
-
       //Field name finishing with _field : set the value in lower case
       $input['_login_field'] = 'TEST';
       $result         = $ldap->prepareInputForUpdate($input);
@@ -598,4 +576,48 @@ class AuthLDAP extends DbTestCase {
       $infos = ['objectguid' => 'value'];
       $this->string(\AuthLDAP::getFieldValue($infos, 'objectguid'))->isIdenticalTo('value');
    }
+
+   public function testPassword() {
+      $ldap = new \AuthLDAP();
+      $id = (int)$ldap->add([
+         'name'        => 'LDAPcrypted',
+         'is_active'   => 1,
+         'is_default'  => 0,
+         'basedn'      => 'ou=people,dc=mycompany',
+         'login_field' => 'uid',
+         'phone_field' => 'phonenumber'
+      ]);
+      $this->integer($id)->isGreaterThan(0);
+
+      //rootdn_passwd is set with a value (a password, not encrypted)
+      $password = 'toto';
+      $input    = ['id' => $id, 'name' => 'ldap', 'rootdn_passwd' => $password];
+      $this->boolean($ldap->update($input))->isTrue();
+      $this->boolean($ldap->getFromDB($id))->isTrue();
+
+      //Expected value to be encrypted using current  key
+      $this->string(\Toolbox::sodiumDecrypt($ldap->fields['rootdn_passwd']))->isIdenticalTo($password);
+
+      $password = 'tot\'o';
+      $input    = ['id' => $id, 'name' => 'ldap', 'rootdn_passwd' => $password];
+      $this->boolean($ldap->update($input))->isTrue();
+      $this->boolean($ldap->getFromDB($id))->isTrue();
+
+      //Expected value to be encrypted using current key
+      $this->string(\Toolbox::sodiumDecrypt($ldap->fields['rootdn_passwd']))->isIdenticalTo($password);
+
+      $password = 'tot\'o';
+      $input    = ['id' => $id, 'name' => 'ldap', 'rootdn_passwd' => \Toolbox::addslashes_deep($password)];
+      $this->boolean($ldap->update($input))->isTrue();
+      $this->boolean($ldap->getFromDB($id))->isTrue();
+
+      //Expected value to be encrypted using current key
+      $this->string(\Toolbox::sodiumDecrypt($ldap->fields['rootdn_passwd']))->isIdenticalTo($password);
+
+      $input['_blank_passwd'] = 1;
+      $result   = $ldap->prepareInputForUpdate($input);
+      //rootdn_passwd is set but empty
+      $this->string($result['rootdn_passwd'])->isEmpty();
+
+   }
 }

programming_language: PHP
filename: MailCollector.php
change_type: ModificationType.MODIFY
diff: @@ -116,7 +116,7 @@ class MailCollector extends DbTestCase {
 
       $this->array($this->testedInstance->prepareInput($oinput, 'add'))
          ->isIdenticalTo([
-            'passwd'    => \Toolbox::encrypt($oinput["passwd"], GLPIKEY),
+            'passwd'    => \Toolbox::sodiumEncrypt($oinput["passwd"]),
             'is_active' => true
          ]);
 
@@ -369,4 +369,4 @@ class MailCollector extends DbTestCase {
       }
        */
    }
-}
\ No newline at end of file
+}

programming_language: PHP
filename: Toolbox.php
change_type: ModificationType.MODIFY
diff: @@ -347,18 +347,21 @@ class Toolbox extends \GLPITestCase {
       ];
    }
 
-   /**
-    * @dataProvider encryptProvider
-    */
-   public function testEncrypt($string, $key, $expected) {
-      $this->string(\Toolbox::encrypt($string, $key))->isIdenticalTo($expected);
+   protected function sodiumEncryptProvider() {
+      return [
+         ['My string'],
+         ['keepmysecret'],
+         ['This is a strng I want to crypt, with some unusual chars like %, \', @, and so on!']
+      ];
    }
 
    /**
-    * @dataProvider encryptProvider
+    * @dataProvider sodiumEncryptProvider
     */
-   public function testDecrypt($expected, $key, $string) {
-      $this->string(\Toolbox::decrypt($string, $key))->isIdenticalTo($expected);
+   public function testSodiumEncrypt($string) {
+      $crypted = \Toolbox::sodiumEncrypt($string);
+      $this->string($crypted)->isNotEmpty();
+      $this->string(\Toolbox::sodiumDecrypt($crypted))->isIdenticalTo($string);
    }
 
    protected function cleanProvider() {

programming_language: PHP