4271 Joerg Bruehe 2012-09-26
RPM spec file: call 'mysql_install_db --random-passwords'
This sets a random root password for each RPM installation
(bug# 12794345).
modified:
support-files/mysql.spec.sh
4270 Joerg Bruehe 2012-09-26
Fix for bug# 12794345:
Ensure a random password is set for the root user
The approach is that the script "mysql_install_db" (Perl variant)
supports a new option "--random-passwords".
If this option is set, the script will (after setting the
predefined accounts in the table "mysql.user") run commands
from "mysql_security_commands.sql" (new file).
These commands will set a random password for the root accounts
and set the "password expired" flag,
and they will remove the anonymous accounts.
@ scripts/CMakeLists.txt
Handle the new file: mysql_security_commands.sql
@ scripts/mysql_install_db.pl.in
The new option "--random-passwords" is not yet
implemented for Windows.
@ scripts/mysql_security_commands.sql
File with SQL commands to enhance the protection of a deployment.
Note that the constant password here must be replaced
by a truly random password in any script that runs these
commands against a MySQL server.
added:
scripts/mysql_security_commands.sql
modified:
scripts/CMakeLists.txt
scripts/mysql_install_db.pl.in
4269 Joerg Bruehe 2012-09-24
Further fix the Perl variant of "mysql_install_db":
1) "resolveip" must explicitly be called via its location
in "$extra_bindir/" to be found,
2) system tables for "current_hostname" must not be suppressed
unless it is a bootstrap run,
3) and options controlling the default file must keep their name.
modified:
scripts/mysql_install_db.pl.in
=== modified file 'scripts/CMakeLists.txt'
--- a/scripts/CMakeLists.txt 2012-09-11 18:10:34 +0000
+++ b/scripts/CMakeLists.txt 2012-09-26 19:37:26 +0000
@@ -76,6 +76,7 @@ INSTALL(FILES
${CMAKE_CURRENT_SOURCE_DIR}/mysql_system_tables_data.sql
${CMAKE_CURRENT_SOURCE_DIR}/fill_help_tables.sql
${CMAKE_CURRENT_SOURCE_DIR}/mysql_test_data_timezone.sql
+ ${CMAKE_CURRENT_SOURCE_DIR}/mysql_security_commands.sql
${FIX_PRIVILEGES_SQL}
DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server
)
=== modified file 'scripts/mysql_install_db.pl.in'
--- a/scripts/mysql_install_db.pl.in 2012-09-24 20:27:28 +0000
+++ b/scripts/mysql_install_db.pl.in 2012-09-26 19:37:26 +0000
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# -*- cperl -*-
#
-# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,6 +34,7 @@
#
##############################################################################
+use Fcntl;
use File::Basename;
use Getopt::Long;
use Sys::Hostname;
@@ -70,6 +71,9 @@ Usage: $0 [OPTIONS]
--help Display this help and exit.
--ldata=path The path to the MySQL data directory. Same as --datadir.
--no-defaults Don't read default options from any option file.
+ --random-passwords Create and set a random password for all root accounts
+ and set the "password expired" flag,
+ remove the anonymous accounts.
--rpm For internal use. This option is used by RPM files
during the MySQL installation process.
--skip-name-resolve Use IP addresses rather than hostnames when creating
@@ -132,8 +136,9 @@ sub parse_arguments
"verbose",
"rpm",
"help",
+ "random-passwords",
- # These options will also be pased to mysqld.
+ # These options will also be pased to mysqld.
"defaults-file=s",
"defaults-extra-file=s",
"no-defaults",
@@ -147,7 +152,7 @@ sub parse_arguments
# the install package. See top-level 'dist-hook' make target.
#
# --windows is a deprecated alias
- "cross-bootstrap|windows", # FIXME undocumented, even needed?
+ "cross-bootstrap|windows",
) or usage();
usage() if $opt->{help};
@@ -251,6 +256,118 @@ sub quote_options {
##############################################################################
#
+# Simple escape mechanism (\-escape any ' and \), suitable for two contexts:
+# - single-quoted SQL strings
+# - single-quoted option values on the right hand side of = in my.cnf
+#
+##############################################################################
+
+# (Function and comment copied from 'mysql_secure_installation')
+
+# These two contexts don't handle escapes identically. SQL strings allow
+# quoting any character (\C => C, for any C), but my.cnf parsing allows
+# quoting only \, ' or ". For example, password='a\b' quotes a 3-character
+# string in my.cnf, but a 2-character string in SQL.
+#
+# This simple escape works correctly in both places.
+
+# FIXME: What about double quote in password? Not handled here - not needed?
+
+sub basic_single_escape {
+ my ($str) = @_;
+ # Inside a character class, \ is not special; this escapes both \ and '
+ $str =~ s/([\'])/\\$1/g;
+ return $str;
+}
+
+##############################################################################
+#
+# Handle the files with confidential contents
+#
+##############################################################################
+
+my $secret_file; # full path name of the confidential file
+my $escaped_password; # the password, with special characters escaped
+
+sub ensure_secret_file {
+ $secret_file = $ENV{HOME} . "/.mysql_secret";
+
+ # Create safe files to avoid leaking info to other users
+ # Loop may be extended if we need more ...
+ foreach my $file ( $secret_file ) {
+ next if -f $file; # Already exists
+ local *FILE;
+ sysopen(FILE, $file, O_CREAT, 0600)
+ or die "ERROR: can't create $file: $!";
+ close FILE;
+ }
+}
+
+##############################################################################
+#
+# Append an arbitrary number of lines to an existing file
+#
+##############################################################################
+
+sub append_file {
+ my $file = shift;
+ -f $file or die "ERROR: file is missing \"$file\": $!";
+ open(FILE, ">>$file") or die "ERROR: can't append to file \"$file\": $!";
+ foreach my $line ( @_ ) {
+ print FILE $line, "\n"; # Add EOL char
+ }
+ close FILE;
+}
+
+##############################################################################
+#
+# Inform the user about the generated random password
+#
+##############################################################################
+
+sub tell_root_password {
+ my $now = localtime(); # scalar context = printable string
+
+ # Now, we need to tell the user the new root password.
+ # We use "append_file" to protect the user in case they are doing multiple
+ # installations intermixed with backups and restores.
+ # While this would be really bad practice, it still might happen.
+ # As long as this file is not destroyed, the time stamps may rescue them.
+ append_file($secret_file,
+ "# The random password set for the root user at $now (local time):",
+ $escaped_password,
+ "");
+ print "A random root password has been set. You will find it in '$secret_file'.\n";
+}
+
+##############################################################################
+#
+# Generate a random password
+#
+##############################################################################
+
+sub generate_random_password {
+ # Short term:
+ # On (at least) Linux and Solaris, a "random" device is available, use it:
+ # cat /dev/urandom | tr -dc "[:alnum:]" | fold -w 8 | head -1
+ # Note: There is no guarantee the results will pass a validation checker
+ # as resulted from WL#2739
+ # http://wl.no.oracle.com/worklo/?tid=2739
+ # http://dev.mysql.com/doc/refman/5.6/en/validate-password-plugin.html
+ #
+ # Long term:
+ # Password generated via Perl module "String::MkPasswd" available at
+ # http://search.cpan.org/~cgrau/String-MkPasswd/bin/mkpasswd.pl
+ # Using it got approved recently.
+ #
+ my $password = `cat /dev/urandom | tr -dc "[:alnum:]" | fold -w 8 | head -1`;
+ chomp ($password);
+ return $password;
+}
+
+
+##############################################################################
+#
# Ok, let's go. We first need to parse arguments which are required by
# my_print_defaults so that we can execute it first, then later re-parse
# the command line to add any extra bits that we need.
@@ -313,6 +430,24 @@ parse_arguments($opt, @default_options);
parse_arguments($opt, 'PICK-ARGS-FROM-ARGV', @ARGV);
# ----------------------------------------------------------------------
+# Create a random password for root, if requested and implemented
+# ----------------------------------------------------------------------
+
+if ( $opt->{'random-passwords'} ) {
+ # Add other non-working OS like this: $^O =~ m/^(solaris|linux|freebsd|darwin)$/
+ # Issue 1: random password creation
+ # Issue 2: confidential file
+ if ( $^O =~ m/^(MSWin32)$/ ) {
+ print "Random password not yet implemented for $^O - option will be ignored\n";
+ delete $opt->{'random-passwords'};
+ } else {
+ ensure_secret_file();
+ my $password = generate_random_password();
+ $escaped_password = basic_single_escape($password);
+ }
+}
+
+# ----------------------------------------------------------------------
# Configure paths to support files
# ----------------------------------------------------------------------
@@ -368,8 +503,9 @@ if ( $opt->{srcdir} )
my $fill_help_tables = "$pkgdatadir/fill_help_tables.sql";
my $create_system_tables = "$pkgdatadir/mysql_system_tables.sql";
my $fill_system_tables = "$pkgdatadir/mysql_system_tables_data.sql";
+my $security_commands = "$pkgdatadir/mysql_security_commands.sql";
-foreach my $f ( $fill_help_tables,$create_system_tables,$fill_system_tables )
+foreach my $f ( $fill_help_tables, $create_system_tables, $fill_system_tables, $security_commands )
{
-f $f or cannot_find_file($f);
}
@@ -444,7 +580,6 @@ my $mysqld_install_cmd_line = quote_opti
"--basedir=$opt->{basedir}",
"--datadir=$opt->{ldata}",
"--log-warnings=0",
-# Not working in 5.6.8: "--loose-skip-innodb",
"--loose-skip-ndbcluster",
"--max_allowed_packet=8M",
"--default-storage-engine=MyISAM",
@@ -484,6 +619,21 @@ if ( open(PIPE, "| $mysqld_install_cmd_l
print PIPE $_;
}
+
+ if ( $opt->{'random-passwords'} )
+ {
+ open(SQL3, $security_commands)
+ or error($opt,"can't open $security_commands for reading: $!");
+ while ( <SQL3> )
+ {
+ # using the implicit variable $_ !
+ s/ABC123xyz/$escaped_password/e ; # Replace placeholder by random password
+ print PIPE $_;
+ }
+ close SQL3;
+ tell_root_password();
+ }
+
close PIPE;
close SQL;
close SQL2;
=== added file 'scripts/mysql_security_commands.sql'
--- a/scripts/mysql_security_commands.sql 1970-01-01 00:00:00 +0000
+++ b/scripts/mysql_security_commands.sql 2012-09-26 19:37:26 +0000
@@ -0,0 +1,36 @@
+-- Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+--
+-- This program 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; version 2 of the License.
+--
+-- This program 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 this program; if not, write to the Free Software
+-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# This set of commands will modify the predefined accounts of a MySQL installation
+# to increase security.
+
+# 1) Set passwords for the root account.
+# Note that the password 'ABC123xyz' will be replaced by a random string
+# when these commands are transferred to the server.
+SET @@old_passwords=1;
+UPDATE mysql.user SET Password=PASSWORD('ABC123xyz') WHERE User='root' and plugin='mysql_old_password';
+SET @@old_passwords=0;
+UPDATE mysql.user SET Password=PASSWORD('ABC123xyz') WHERE User='root' and plugin in ('', 'mysql_native_password');
+SET @@old_passwords=2;
+UPDATE mysql.user SET authentication_string=PASSWORD('ABC123xyz') WHERE User='root' and plugin='sha256_password';
+
+# 2) Drop the anonymous account.
+DELETE FROM mysql.user WHERE User='';
+
+# 3) Force the root user to change the password on first connect.
+UPDATE mysql.user SET Password_expired='Y' WHERE User='root';
+
+# In case this file is sent to a running server.
+FLUSH PRIVILEGES;
=== modified file 'support-files/mysql.spec.sh'
--- a/support-files/mysql.spec.sh 2012-08-22 12:30:48 +0000
+++ b/support-files/mysql.spec.sh 2012-09-26 19:44:13 +0000
@@ -1,4 +1,4 @@
-# Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -787,7 +787,13 @@ if ! grep '^MySQL RPM upgrade' $STATUS_F
# Fix bug#45415: no "mysql_install_db" on an upgrade
# Do this as a negative to err towards more "install" runs
# rather than to miss one.
- %{_bindir}/mysql_install_db --rpm --user=%{mysqld_user}
+ %{_bindir}/mysql_install_db --rpm --user=%{mysqld_user} --random-passwords
+
+ # Attention: Now 'root' is the only database user,
+ # its password is a random value found in ~/.mysql_secret,
+ # and the "password expired" flag is set:
+ # Any client needs that password, and the first command
+ # executed must be a new "set password"!
fi
# ----------------------------------------------------------------------
@@ -1136,6 +1142,12 @@ echo "====="
# merging BK trees)
##############################################################################
%changelog
+* Wed Sep 26 2012 Joerg Bruehe <joerg.bruehe@stripped>
+
+- Let the installation use the new option "--random-passwords" of
+ "mysql_install_db".
+ (Bug# 12794345 Ensure root password)
+
* Tue Jul 24 2012 Joerg Bruehe <joerg.bruehe@stripped>
- Add a macro "runselftest":
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-5.6 branch (joerg.bruehe:4269 to 4271) | Joerg Bruehe | 26 Sep |