diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php index 11163806abfc75774d41b0880a53c67b6ac6fdc4..70ce20c7c8c1b013e02e31117fc06e746497ea00 100644 --- a/install/lib/installer_base.lib.php +++ b/install/lib/installer_base.lib.php @@ -235,21 +235,27 @@ class installer_base { public function configure_database() { global $conf; - //* check sql-mode - /*$check_sql_mode = $this->db->queryOneRecord("SELECT @@sql_mode"); - - if ($check_sql_mode['@@sql_mode'] != '' && $check_sql_mode['@@sql_mode'] != 'NO_ENGINE_SUBSTITUTION') { - echo "Wrong SQL-mode. You should use NO_ENGINE_SUBSTITUTION. Add\n\n"; - echo " sql-mode=\"NO_ENGINE_SUBSTITUTION\"\n\n"; - echo"to the mysqld-section in your mysql-config on this server and restart mysqld afterwards\n"; - die(); - }*/ - - $unwanted_sql_plugins = array('validate_password'); - $sql_plugins = $this->db->queryAllRecords("SELECT plugin_name FROM information_schema.plugins WHERE plugin_status='ACTIVE' AND plugin_name IN ?", $unwanted_sql_plugins); - if(is_array($sql_plugins) && !empty($sql_plugins)) { - foreach ($sql_plugins as $plugin) echo "Login in to MySQL and disable $plugin[plugin_name] with:\n\n UNINSTALL PLUGIN $plugin[plugin_name];"; - die(); + //** Check for unwanted plugins + if ($this->db->getDatabaseType() == 'mysql' && $this->db->getDatabaseVersion(true) >= 8) { + // component approach since MySQL 8.0 + $unwanted_components = [ + 'file://component_validate_password', + ]; + $sql_components = $this->db->queryAllRecords("SELECT * FROM mysql.component where component_urn IN ?", $unwanted_components); + if(is_array($sql_components) && !empty($sql_components)) { + foreach ($sql_components as $component) { + $component_name = parse_url($component['component_urn'], PHP_URL_HOST); + echo "Login in to MySQL and disable '{$component_name}' with:\n\n UNINSTALL COMPONENT '{$component['component_urn']}';\n\n"; + } + die(); + } + } else { + $unwanted_sql_plugins = array('validate_password'); + $sql_plugins = $this->db->queryAllRecords("SELECT plugin_name FROM information_schema.plugins WHERE plugin_status='ACTIVE' AND plugin_name IN ?", $unwanted_sql_plugins); + if(is_array($sql_plugins) && !empty($sql_plugins)) { + foreach ($sql_plugins as $plugin) echo "Login in to MySQL and disable $plugin[plugin_name] with:\n\n UNINSTALL PLUGIN $plugin[plugin_name];"; + die(); + } } //** Create the database @@ -308,6 +314,15 @@ class installer_base { if(!$this->db->query($query, $conf['mysql']['database'] . ".*", $conf['mysql']['ispconfig_user'], $from_host)) { $this->error('Unable to grant databse permissions to user: '.$conf['mysql']['ispconfig_user'].' Error: '.$this->db->errorMessage); } + + // add correct administrative rights to IPSConfig user (SUPER is deprecated and unnecessarily powerful) + if ($this->db->getDatabaseType() == 'mysql' && $this->db->getDatabaseVersion(true) >= 8) { + // there might be more needed on replicated db environments, this was not tested + $query = 'GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO ?@?'; + if(!$this->db->query($query, $conf['mysql']['ispconfig_user'], $from_host)) { + $this->error('Unable to grant administrative permissions to user: '.$conf['mysql']['ispconfig_user'].' Error: '.$this->db->errorMessage); + } + } //* Set the database name in the DB library $this->db->setDBName($conf['mysql']['database']); diff --git a/install/lib/mysql.lib.php b/install/lib/mysql.lib.php index c24a454d040e0d5bb6b4d8b613da99a9aae04158..1085ed0d5b2c04cd97bcc5968f371afec5e0dc35 100644 --- a/install/lib/mysql.lib.php +++ b/install/lib/mysql.lib.php @@ -761,6 +761,41 @@ class db break; } } + + /** + * Get the database type (mariadb or mysql) + * + * @access public + * @return string 'mariadb' or string 'mysql' + */ + + public function getDatabaseType() { + $tmp = $this->queryOneRecord('SELECT VERSION() as version'); + if(stristr($tmp['version'],'mariadb')) { + return 'mariadb'; + } else { + return 'mysql'; + } + } + + /** + * Get the database version + * + * @access public + * @param bool $major_version_only = true will return the major version only, e.g. 8 for MySQL 8 + * @return string version number + */ + + public function getDatabaseVersion($major_version_only = false) { + $tmp = $this->queryOneRecord('SELECT VERSION() as version'); + $version = explode('-', $tmp['version']); + if($major_version_only == true) { + $version_parts = explode('.', $version[0]); + return $version_parts[0]; + } else { + return $version[0]; + } + } } diff --git a/install/sql/incremental/upd_dev_collection.sql b/install/sql/incremental/upd_dev_collection.sql index f6bd9982f798916b7a214dd7b878995730ac41a8..2f3d7568e85bd715faa8de97a525606ed73481f0 100644 --- a/install/sql/incremental/upd_dev_collection.sql +++ b/install/sql/incremental/upd_dev_collection.sql @@ -37,7 +37,7 @@ ALTER TABLE `mail_user` ADD `disableindexer-worker` ENUM('n','y') CHARACTER SET ALTER TABLE `dns_rr` CHANGE `type` `type` ENUM('A','AAAA','ALIAS','CNAME','DNAME','CAA','DS','HINFO','LOC','MX','NAPTR','NS','PTR','RP','SRV','SSHFP','TXT','TLSA','DNSKEY') NULL DEFAULT NULL AFTER `name`; -- change cc and sender_cc column type -ALTER TABLE `mail_user` CHANGE `cc` `cc` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''; +ALTER TABLE `mail_user` CHANGE `cc` `cc` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci; -- remove SPDY option ALTER TABLE `web_domain` DROP COLUMN `enable_spdy`; @@ -62,7 +62,7 @@ ALTER TABLE `web_domain` CHANGE `nginx_directives` `nginx_directives` mediumtext ALTER TABLE `mail_user` MODIFY `move_junk` enum('y','a','n') NOT NULL DEFAULT 'y'; -- Change id_rsa column to TEXT format -ALTER TABLE `client` CHANGE `id_rsa` `id_rsa` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''; +ALTER TABLE `client` CHANGE `id_rsa` `id_rsa` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci; ALTER TABLE `directive_snippets` ADD `update_sites` ENUM('y','n') NOT NULL DEFAULT 'n' ; diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql index 095a2d37de445c0fbeec796055d3fb685355fb64..90c39c03a73b73ba749e96c0f8fa57e3ed777e2b 100644 --- a/install/sql/ispconfig3.sql +++ b/install/sql/ispconfig3.sql @@ -253,7 +253,7 @@ CREATE TABLE `client` ( `canceled` enum('n','y') NOT NULL DEFAULT 'n', `can_use_api` enum('n','y') NOT NULL DEFAULT 'n', `tmp_data` mediumblob, - `id_rsa` text NOT NULL DEFAULT '', + `id_rsa` text, `ssh_rsa` varchar(600) NOT NULL DEFAULT '', `customer_no_template` varchar(255) DEFAULT 'R[CLIENTID]C[CUSTOMER_NO]', `customer_no_start` int(11) NOT NULL DEFAULT '1', @@ -1040,7 +1040,7 @@ CREATE TABLE `mail_user` ( `maildir` varchar(255) NOT NULL default '', `maildir_format` varchar(255) NOT NULL default 'maildir', `quota` bigint(20) NOT NULL default '-1', - `cc` text NOT NULL default '', + `cc` text, `sender_cc` varchar(255) NOT NULL default '', `homedir` varchar(255) NOT NULL default '', `autoresponder` enum('n','y') NOT NULL default 'n', diff --git a/interface/lib/classes/db_mysql.inc.php b/interface/lib/classes/db_mysql.inc.php index 014feec8c319cef6fa5585c5cc91fc3185cc5490..feab66cd936bbbbb8df15d49e39c9564fb13e6b0 100644 --- a/interface/lib/classes/db_mysql.inc.php +++ b/interface/lib/classes/db_mysql.inc.php @@ -1106,6 +1106,76 @@ class db } } + /** + * Get the database type (mariadb or mysql) + * + * @access public + * @return string 'mariadb' or string 'mysql' + */ + + public function getDatabaseType() { + $tmp = $this->queryOneRecord('SELECT VERSION() as version'); + if(stristr($tmp['version'],'mariadb')) { + return 'mariadb'; + } else { + return 'mysql'; + } + } + + /** + * Get the database version + * + * @access public + * @param bool $major_version_only = true will return the major version only, e.g. 8 for MySQL 8 + * @return string version number + */ + + public function getDatabaseVersion($major_version_only = false) { + $tmp = $this->queryOneRecord('SELECT VERSION() as version'); + $version = explode('-', $tmp['version']); + if($major_version_only == true) { + $version_parts = explode('.', $version[0]); + return $version_parts[0]; + } else { + return $version[0]; + } + } + + /** + * Get a mysql password hash + * + * @access public + * @param string cleartext password + * @return string Password hash + */ + + public function getPasswordHash($password) { + + $password_type = 'password'; + + /* Disabled until caching_sha2_password is implemented + if($this->getDatabaseType() == 'mysql' && $this->getDatabaseVersion(true) >= 8) { + // we are in MySQL 8 mode + $tmp = $this->queryOneRecord("show variables like 'default_authentication_plugin'"); + if($tmp['default_authentication_plugin'] == 'caching_sha2_password') { + $password_type = 'caching_sha2_password'; + } + } + */ + + if($password_type == 'caching_sha2_password') { + /* + caching_sha2_password hashing needs to be implemented, have not + found valid PHP implementation for the new password hash type. + */ + } else { + $password_hash = '*'.strtoupper(sha1(sha1($password, true))); + } + + return $password_hash; + } + + } /** diff --git a/interface/lib/classes/tform_base.inc.php b/interface/lib/classes/tform_base.inc.php index cbbb83ee9c7d8ee9860725c5650ecb80f1ace805..91a855872c9600939ac338f2b5cbe5bd11513d73 100644 --- a/interface/lib/classes/tform_base.inc.php +++ b/interface/lib/classes/tform_base.inc.php @@ -1358,8 +1358,7 @@ class tform_base { $record[$key] = $app->auth->crypt_password(stripslashes($record[$key]),'ISO-8859-1'); $sql_insert_val .= "'".$app->db->quote($record[$key])."', "; } elseif (isset($field['encryption']) && $field['encryption'] == 'MYSQL') { - $tmp = $app->db->queryOneRecord("SELECT PASSWORD(?) as `crypted`", stripslashes($record[$key])); - $record[$key] = $tmp['crypted']; + $record[$key] = $app->db->getPasswordHash($record[$key]); $sql_insert_val .= "'".$app->db->quote($record[$key])."', "; } else { $record[$key] = md5(stripslashes($record[$key])); @@ -1390,8 +1389,7 @@ class tform_base { $record[$key] = $app->auth->crypt_password(stripslashes($record[$key]),'ISO-8859-1'); $sql_update .= "`$key` = '".$app->db->quote($record[$key])."', "; } elseif (isset($field['encryption']) && $field['encryption'] == 'MYSQL') { - $tmp = $app->db->queryOneRecord("SELECT PASSWORD(?) as `crypted`", stripslashes($record[$key])); - $record[$key] = $tmp['crypted']; + $record[$key] = $app->db->getPasswordHash($record[$key]); $sql_update .= "`$key` = '".$app->db->quote($record[$key])."', "; } else { $record[$key] = md5(stripslashes($record[$key])); diff --git a/server/lib/classes/db_mysql.inc.php b/server/lib/classes/db_mysql.inc.php index 8c381230961976b5152ae5bff25cd7fa00f3634a..df38086ebee7ff73d67ba437c36e7172b7a15c77 100644 --- a/server/lib/classes/db_mysql.inc.php +++ b/server/lib/classes/db_mysql.inc.php @@ -1106,6 +1106,75 @@ class db } } + /** + * Get the database type (mariadb or mysql) + * + * @access public + * @return string 'mariadb' or string 'mysql' + */ + + public function getDatabaseType() { + $tmp = $this->queryOneRecord('SELECT VERSION() as version'); + if(stristr($tmp['version'],'mariadb')) { + return 'mariadb'; + } else { + return 'mysql'; + } + } + + /** + * Get the database version + * + * @access public + * @param bool $major_version_only = true will return the major version only, e.g. 8 for MySQL 8 + * @return string version number + */ + + public function getDatabaseVersion($major_version_only = false) { + $tmp = $this->queryOneRecord('SELECT VERSION() as version'); + $version = explode('-', $tmp['version']); + if($major_version_only == true) { + $version_parts = explode('.', $version[0]); + return $version_parts[0]; + } else { + return $version[0]; + } + } + + /** + * Get a mysql password hash + * + * @access public + * @param string cleartext password + * @return string Password hash + */ + + public function getPasswordHash($password) { + + $password_type = 'password'; + + /* Disabled until caching_sha2_password is implemented + if($this->getDatabaseType() == 'mysql' && $this->getDatabaseVersion(true) >= 8) { + // we are in MySQL 8 mode + $tmp = $this->queryOneRecord("show variables like 'default_authentication_plugin'"); + if($tmp['default_authentication_plugin'] == 'caching_sha2_password') { + $password_type = 'caching_sha2_password'; + } + } + */ + + if($password_type == 'caching_sha2_password') { + /* + caching_sha2_password hashing needs to be implemented, have not + found valid PHP implementation for the new password hash type. + */ + } else { + $password_hash = '*'.strtoupper(sha1(sha1($password, true))); + } + + return $password_hash; + } + } /** diff --git a/server/plugins-available/mysql_clientdb_plugin.inc.php b/server/plugins-available/mysql_clientdb_plugin.inc.php index e1fba6e18082c4fda64addbb5a293bc3b9940f95..f28e6006ce1de08ae7bbe6e0d48aea0206bcf0e4 100644 --- a/server/plugins-available/mysql_clientdb_plugin.inc.php +++ b/server/plugins-available/mysql_clientdb_plugin.inc.php @@ -101,12 +101,7 @@ class mysql_clientdb_plugin { $success = true; if(!preg_match('/\*[A-F0-9]{40}$/', $database_password)) { - $result = $link->query("SELECT PASSWORD('" . $link->escape_string($database_password) . "') as `crypted`"); - if($result) { - $row = $result->fetch_assoc(); - $database_password = $row['crypted']; - $result->free(); - } + $database_password = $app->db->getPasswordHash($password); } $app->log("Calling $action for $database_name with access $user_access_mode and hosts " . implode(', ', $host_list), LOGLEVEL_DEBUG); @@ -151,9 +146,32 @@ class mysql_clientdb_plugin { $success = true; } - if(!$link->query("GRANT " . $grants . " ON `".$database_name."`.* TO '".$link->escape_string($database_user)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($database_password)."'")) $success = false; - $app->log("GRANT " . $grants . " ON `".$database_name."`.* TO '".$link->escape_string($database_user)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($database_password)."' success? " . ($success ? 'yes' : 'no'), LOGLEVEL_DEBUG); - } elseif($action == 'REVOKE') { + // Create the user + $link->query("CREATE USER '".$link->escape_string($database_user)."'@'$db_host'"); + $app->log("CREATE USER '".$link->escape_string($database_user)."'@'$db_host'", LOGLEVEL_DEBUG); + + // set the password + // MySQL < 5.7 and MariadB 10 + if(!$link->query("UPDATE mysql.user SET `Password` = '".$link->escape_string($database_password)."' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) { + if($this->getDatabaseType($link) == 'mysql' && $this->getDatabaseVersion($link, true) >= 8) { + // for MySQL >= 8, we set authentication plugin to old mode to ensure that older additional php versions can still connect to the database + if(!$link->query("UPDATE mysql.user SET `authentication_string` = '".$link->escape_string($database_password)."', `plugin` = 'mysql_native_password' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) $success = false; + } else { + // MySQL 5.7, the Password field has been renamed to authentication_string + if(!$link->query("UPDATE mysql.user SET `authentication_string` = '".$link->escape_string($database_password)."' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) $success = false; + } + } + + if($success == true){ + $link->query("FLUSH PRIVILEGES"); + $app->log("PASSWORD SET FOR '".$link->escape_string($database_user)."'@'$db_host' success? " . ($success ? 'yes' : 'no'), LOGLEVEL_DEBUG); + } + + // Set the grant + if(!$link->query("GRANT " . $grants . " ON `".$link->escape_string($database_name)."`.* TO '".$link->escape_string($database_user)."'@'$db_host'")) $success = false; + $app->log("GRANT " . $grants . " ON `".$link->escape_string($database_name)."`.* TO '".$link->escape_string($database_user)."'@'$db_host' success? " . ($success ? 'yes' : 'no'), LOGLEVEL_DEBUG); + + } elseif($action == 'REVOKE') { if(!$link->query("REVOKE ALL PRIVILEGES ON `".$database_name."`.* FROM '".$link->escape_string($database_user)."'@'$db_host'")) $success = false; } elseif($action == 'DROP') { if(!$link->query("DROP USER '".$link->escape_string($database_user)."'@'$db_host'")) $success = false; @@ -165,8 +183,13 @@ class mysql_clientdb_plugin { if(trim($database_password) != '') { // MySQL < 5.7 and MariadB 10 if(!$link->query("UPDATE mysql.user SET `Password` = '".$link->escape_string($database_password)."' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) { - // MySQL 5.7, the Password field has been renamed to authentication_string - if(!$link->query("UPDATE mysql.user SET `authentication_string` = '".$link->escape_string($database_password)."' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) $success = false; + if($this->getDatabaseType($link) == 'mysql' && $this->getDatabaseVersion($link, true) >= 8) { + // for MySQL >= 8, we set authentication plugin to old mode to ensure that older additional php versions can still connect to the database + if(!$link->query("UPDATE mysql.user SET `authentication_string` = '".$link->escape_string($database_password)."', `plugin` = 'mysql_native_password' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) $success = false; + } else { + // MySQL 5.7, the Password field has been renamed to authentication_string + if(!$link->query("UPDATE mysql.user SET `authentication_string` = '".$link->escape_string($database_password)."' WHERE `Host` = '".$db_host."' AND `User` = '".$link->escape_string($database_user)."'")) $success = false; + } } if($success == true) $link->query("FLUSH PRIVILEGES"); } @@ -772,6 +795,43 @@ class mysql_clientdb_plugin { $link->close(); } + + + + + function getDatabaseType($link) { + $result = $link->query('SELECT VERSION() as version'); + if($result) { + $tmp = $result->fetch_assoc(); + $result->free(); + + if(stristr($tmp['version'],'mariadb')) { + return 'mariadb'; + } else { + return 'mysql'; + } + } else { + return false; + } + } + + function getDatabaseVersion($link, $major_version_only = false) { + $result = $link->query('SELECT VERSION() as version'); + if($result) { + $tmp = $result->fetch_assoc(); + $result->free(); + + $version = explode('-', $tmp['version']); + if($major_version_only == true) { + $version_parts = explode('.', $version[0]); + return $version_parts[0]; + } else { + return $version[0]; + } + } else { + return false; + } + } } // end class