Unverified Commit f1ae6c84 authored by Johan Cwiklinski's avatar Johan Cwiklinski Committed by GitHub
Browse files

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
parent 8d48dc4d
/config/config_db*
/config/glpi.key
/config/glpicrypt.key
/config/local_define.php
/tests/config_db*
/marketplace/
......
......@@ -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
......
......@@ -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"]));
}
}
......
......@@ -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,
......
......@@ -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"
}
......@@ -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
}
......@@ -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;
......
......@@ -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 {
......
......@@ -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
......
<?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
}
}
......@@ -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
}
<?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.
*