List:Commits« Previous MessageNext Message »
From:Timothy Smith Date:November 4 2009 9:10pm
Subject:bzr commit into mysql-5.1-bugteam branch (timothy.smith:3193)
View as plain text  
#At file:///home/tsmith/m/bzr/bugteam/51/ based on revid:joro@stripped

 3193 Timothy Smith	2009-11-04 [merge]
      auto-merge

    modified:
      scripts/mysql_secure_installation.pl.in
      scripts/mysql_secure_installation.sh
=== modified file 'scripts/mysql_secure_installation.pl.in'
--- a/scripts/mysql_secure_installation.pl.in	2007-12-28 21:58:54 +0000
+++ b/scripts/mysql_secure_installation.pl.in	2009-11-03 21:34:01 +0000
@@ -17,16 +17,41 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 use Fcntl;
+use File::Spec;
+use if $^O eq 'MSWin32', 'Term::ReadKey' => qw/ReadMode/;
 use strict;
 
 my $config  = ".my.cnf.$$";
 my $command = ".mysql.$$";
 my $hadpass = 0;
+my $mysql;  # How to call the mysql client
+my $rootpass = "";
 
-# FIXME
-# trap "interrupt" 2
 
-my $rootpass = "";
+$SIG{QUIT} = $SIG{INT} = sub {
+  print "\nAborting!\n\n";
+  echo_on();
+  cleanup();
+  exit 1;
+};
+
+
+END {
+  # Remove temporary files, even if exiting via die(), etc.
+  cleanup();
+}
+
+
+sub read_without_echo {
+  my ($prompt) = @_;
+  print $prompt;
+  echo_off();
+  my $answer = <STDIN>;
+  echo_on();
+  print "\n";
+  chomp($answer);
+  return $answer;
+}
 
 sub echo_on {
   if ($^O eq 'MSWin32') {
@@ -55,6 +80,25 @@ sub write_file {
 }
 
 sub prepare {
+  # Locate the mysql client; look in current directory first, then
+  # in path
+  our $SAVEERR;   # Suppress Perl warning message
+  open SAVEERR, ">& STDERR";
+  close STDERR;
+  for my $m (File::Spec->catfile('bin', 'mysql'), 'mysql') {
+    # mysql --version should always work
+    qx($m --no-defaults --version);
+    next unless $? == 0;
+
+    $mysql = $m;
+    last;
+  }
+  open STDERR, ">& SAVEERR";
+
+  die "Can't find a 'mysql' client in PATH or ./bin\n"
+    unless $mysql;
+
+  # Create safe files to avoid leaking info to other users
   foreach my $file ( $config, $command ) {
     next if -f $file;                   # Already exists
     local *FILE;
@@ -64,30 +108,50 @@ sub prepare {
   }
 }
 
+# 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
+#
+# 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.
+sub basic_single_escape {
+  my ($str) = @_;
+  # Inside a character class, \ is not special; this escapes both \ and '
+  $str =~ s/([\'])/\\$1/g;
+  return $str;
+}
+
 sub do_query {
   my $query   = shift;
   write_file($command, $query);
-  system("mysql --defaults-file=$config < $command");
-  return $?;
+  my $rv = system("$mysql --defaults-file=$config < $command");
+  # system() returns -1 if exec fails (e.g., command not found, etc.); die
+  # in this case because nothing is going to work
+  die "Failed to execute mysql client '$mysql'\n" if $rv == -1;
+  # Return true if query executed OK, or false if there was some problem
+  # (for example, SQL error or wrong password)
+  return ($rv == 0 ? 1 : undef);
 }
 
 sub make_config {
   my $password = shift;
 
+  my $esc_pass = basic_single_escape($rootpass);
   write_file($config,
              "# mysql_secure_installation config file",
              "[mysql]",
              "user=root",
-             "password=$rootpass");
+             "password='$esc_pass'");
 }
 
 sub get_root_password {
-  my $status = 1;
-  while ( $status == 1 ) {
-    echo_off();
-    print "Enter current password for root (enter for none): ";
-    my $password = <STDIN>;
-    echo_on();
+  my $attempts = 3;
+  for (;;) {
+    my $password = read_without_echo("Enter current password for root (enter for none): ");
     if ( $password ) {
       $hadpass = 1;
     } else {
@@ -95,64 +159,56 @@ sub get_root_password {
     }
     $rootpass = $password;
     make_config($rootpass);
-    do_query("");
-    $status = $?;
+    last if do_query("");
+
+    die "Unable to connect to the server as root user, giving up.\n"
+      if --$attempts == 0;
   }
   print "OK, successfully used password, moving on...\n\n";
 }
 
 sub set_root_password {
-  echo_off();
-  print "New password: ";
-  my $password1 = <STDIN>;
-  print "\nRe-enter new password: ";
-  my $password2 = <STDIN>;
-  print "\n";
-  echo_on();
-
-  if ( $password1 eq $password2 ) {
-    print "Sorry, passwords do not match.\n\n";
-    return 1;
-  }
+  my $password1;
+  for (;;) {
+    $password1 = read_without_echo("New password: ");
+
+    if ( !$password1 ) {
+      print "Sorry, you can't use an empty password here.\n\n";
+      next;
+    }
 
-  if ( !$password1 ) {
-    print "Sorry, you can't use an empty password here.\n\n";
-    return 1;
-  }
+    my $password2 = read_without_echo("Re-enter new password: ");
 
-  do_query("UPDATE mysql.user SET Password=PASSWORD('$password1') WHERE User='root';");
-  if ( $? == 0 ) {
-    print "Password updated successfully!\n";
-    print "Reloading privilege tables..\n";
-    if ( !reload_privilege_tables() ) {
-      exit 1;
+    if ( $password1 ne $password2 ) {
+      print "Sorry, passwords do not match.\n\n";
+      next;
     }
-    print "\n";
-    $rootpass = $password1;
-    make_config($rootpass);
-  } else {
-    print "Password update failed!\n";
-    exit 1;
+
+    last;
   }
 
-  return 0;
+  my $esc_pass = basic_single_escape($password1);
+  do_query("UPDATE mysql.user SET Password=PASSWORD('$esc_pass') WHERE User='root';")
+    or die "Password update failed!\n";
+
+  print "Password updated successfully!\n";
+  print "Reloading privilege tables..\n";
+  reload_privilege_tables()
+    or die "Can not continue.\n";
+
+  print "\n";
+  $rootpass = $password1;
+  make_config($rootpass);
 }
 
 sub remove_anonymous_users {
-  do_query("DELETE FROM mysql.user WHERE User='';");
-  if ( $? == 0 ) {
-    print " ... Success!\n";
-  } else {
-    print " ... Failed!\n";
-    exit 1;
-  }
-
-  return 0;
+  do_query("DELETE FROM mysql.user WHERE User='';")
+    or die print " ... Failed!\n";
+  print " ... Success!\n";
 }
 
 sub remove_remote_root {
-  do_query("DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';");
-  if ( $? == 0 ) {
+  if (do_query("DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';")) {
     print " ... Success!\n";
   } else {
     print " ... Failed!\n";
@@ -161,44 +217,31 @@ sub remove_remote_root {
 
 sub remove_test_database {
   print " - Dropping test database...\n";
-  do_query("DROP DATABASE test;");
-  if ( $? == 0 ) {
+  if (do_query("DROP DATABASE test;")) {
     print " ... Success!\n";
   } else {
     print " ... Failed!  Not critical, keep moving...\n";
   }
 
   print " - Removing privileges on test database...\n";
-  do_query("DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'");
-  if ( $? == 0 ) {
+  if (do_query("DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'")) {
     print " ... Success!\n";
   } else {
     print " ... Failed!  Not critical, keep moving...\n";
   }
-
-  return 0;
 }
 
 sub reload_privilege_tables {
-  do_query("FLUSH PRIVILEGES;");
-  if ( $? == 0 ) {
+  if (do_query("FLUSH PRIVILEGES;")) {
     print " ... Success!\n";
-    return 0;
+    return 1;
   } else {
     print " ... Failed!\n";
-    return 1;
+    return undef;
   }
 }
 
-sub interrupt {
-  print "\nAborting!\n\n";
-  cleanup();
-  echo_on();
-  exit 1;
-}
-
 sub cleanup {
-  print "Cleaning up...\n";
   unlink($config,$command);
 }
 
@@ -242,11 +285,7 @@ my $reply = <STDIN>;
 if ( $reply =~ /n/i ) {
   print " ... skipping.\n";
 } else {
-  my $status = 1;
-  while ( $status == 1 ) {
-    set_root_password();
-    $status = $?;
-  }
+  set_root_password();
 }
 print "\n";
 
@@ -334,8 +373,6 @@ if ( $reply =~ /n/i ) {
 }
 print "\n";
 
-cleanup();
-
 print <<HERE;
 
 

=== modified file 'scripts/mysql_secure_installation.sh'
--- a/scripts/mysql_secure_installation.sh	2007-01-01 04:31:23 +0000
+++ b/scripts/mysql_secure_installation.sh	2009-11-03 20:50:28 +0000
@@ -38,16 +38,39 @@ prepare() {
 }
 
 do_query() {
-    echo $1 >$command
+    echo "$1" >$command
+    #sed 's,^,> ,' < $command  # Debugging
     mysql --defaults-file=$config <$command
     return $?
 }
 
+# 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
+#
+# 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.
+basic_single_escape () {
+    # The quoting on this sed command is a bit complex.  Single-quoted strings
+    # don't allow *any* escape mechanism, so they cannot contain a single
+    # quote.  The string sed gets (as argv[1]) is:  s/\(['\]\)/\\\1/g
+    #
+    # Inside a character class, \ and ' are not special, so the ['\] character
+    # class is balanced and contains two characters.
+    echo "$1" | sed 's/\(['"'"'\]\)/\\\1/g'
+}
+
 make_config() {
     echo "# mysql_secure_installation config file" >$config
     echo "[mysql]" >>$config
     echo "user=root" >>$config
-    echo "password=$rootpass" >>$config
+    esc_pass=`basic_single_escape "$rootpass"`
+    echo "password='$esc_pass'" >>$config
+    #sed 's,^,> ,' < $config  # Debugging
 }
 
 get_root_password() {
@@ -94,13 +117,12 @@ set_root_password() {
 	return 1
     fi
 
-    do_query "UPDATE mysql.user SET Password=PASSWORD('$password1') WHERE User='root';"
+    esc_pass=`basic_single_escape "$password1"`
+    do_query "UPDATE mysql.user SET Password=PASSWORD('$esc_pass') WHERE User='root';"
     if [ $? -eq 0 ]; then
 	echo "Password updated successfully!"
 	echo "Reloading privilege tables.."
-	if ! reload_privilege_tables; then
-	    exit 1
-	fi
+	reload_privilege_tables || exit 1
 	echo
 	rootpass=$password1
 	make_config


Attachment: [text/bzr-bundle] bzr/timothy.smith@sun.com-20091104210934-cntb1jtwee76ppky.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (timothy.smith:3193)Timothy Smith4 Nov