List:Commits« Previous MessageNext Message »
From:Joerg Bruehe Date:September 26 2012 7:45pm
Subject:bzr push into mysql-5.6 branch (joerg.bruehe:4269 to 4271)
View as plain text  
 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 Bruehe26 Sep