mySQL 5.0.11 sources for tomato
[tomato.git] / release / src / router / mysql / scripts / mysql_secure_installation.pl.in
blob278fffe7322accdc24361dda96e5d72907949c53
1 #!/usr/bin/perl
2 # -*- cperl -*-
4 # Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 use Fcntl;
20 use File::Spec;
21 use if $^O eq 'MSWin32', 'Term::ReadKey' => qw/ReadMode/;
22 use strict;
24 my $config = ".my.cnf.$$";
25 my $command = ".mysql.$$";
26 my $hadpass = 0;
27 my $mysql; # How to call the mysql client
28 my $rootpass = "";
31 $SIG{QUIT} = $SIG{INT} = $SIG{TERM} = $SIG{ABRT} = $SIG{HUP} = sub {
32 print "\nAborting!\n\n";
33 echo_on();
34 cleanup();
35 exit 1;
39 END {
40 # Remove temporary files, even if exiting via die(), etc.
41 cleanup();
45 sub read_without_echo {
46 my ($prompt) = @_;
47 print $prompt;
48 echo_off();
49 my $answer = <STDIN>;
50 echo_on();
51 print "\n";
52 chomp($answer);
53 return $answer;
56 sub echo_on {
57 if ($^O eq 'MSWin32') {
58 ReadMode('normal');
59 } else {
60 system("stty echo");
64 sub echo_off {
65 if ($^O eq 'MSWin32') {
66 ReadMode('noecho');
67 } else {
68 system("stty -echo");
72 sub write_file {
73 my $file = shift;
74 -f $file or die "ERROR: file is missing \"$file\": $!";
75 open(FILE, ">$file") or die "ERROR: can't write to file \"$file\": $!";
76 foreach my $line ( @_ ) {
77 print FILE $line, "\n"; # Add EOL char
79 close FILE;
82 sub prepare {
83 # Locate the mysql client; look in current directory first, then
84 # in path
85 our $SAVEERR; # Suppress Perl warning message
86 open SAVEERR, ">& STDERR";
87 close STDERR;
88 for my $m (File::Spec->catfile('bin', 'mysql'), 'mysql') {
89 # mysql --version should always work
90 qx($m --no-defaults --version);
91 next unless $? == 0;
93 $mysql = $m;
94 last;
96 open STDERR, ">& SAVEERR";
98 die "Can't find a 'mysql' client in PATH or ./bin\n"
99 unless $mysql;
101 # Create safe files to avoid leaking info to other users
102 foreach my $file ( $config, $command ) {
103 next if -f $file; # Already exists
104 local *FILE;
105 sysopen(FILE, $file, O_CREAT, 0600)
106 or die "ERROR: can't create $file: $!";
107 close FILE;
111 # Simple escape mechanism (\-escape any ' and \), suitable for two contexts:
112 # - single-quoted SQL strings
113 # - single-quoted option values on the right hand side of = in my.cnf
115 # These two contexts don't handle escapes identically. SQL strings allow
116 # quoting any character (\C => C, for any C), but my.cnf parsing allows
117 # quoting only \, ' or ". For example, password='a\b' quotes a 3-character
118 # string in my.cnf, but a 2-character string in SQL.
120 # This simple escape works correctly in both places.
121 sub basic_single_escape {
122 my ($str) = @_;
123 # Inside a character class, \ is not special; this escapes both \ and '
124 $str =~ s/([\'])/\\$1/g;
125 return $str;
128 sub do_query {
129 my $query = shift;
130 write_file($command, $query);
131 my $rv = system("$mysql --defaults-file=$config < $command");
132 # system() returns -1 if exec fails (e.g., command not found, etc.); die
133 # in this case because nothing is going to work
134 die "Failed to execute mysql client '$mysql'\n" if $rv == -1;
135 # Return true if query executed OK, or false if there was some problem
136 # (for example, SQL error or wrong password)
137 return ($rv == 0 ? 1 : undef);
140 sub make_config {
141 my $password = shift;
143 my $esc_pass = basic_single_escape($rootpass);
144 write_file($config,
145 "# mysql_secure_installation config file",
146 "[mysql]",
147 "user=root",
148 "password='$esc_pass'");
151 sub get_root_password {
152 my $attempts = 3;
153 for (;;) {
154 my $password = read_without_echo("Enter current password for root (enter for none): ");
155 if ( $password ) {
156 $hadpass = 1;
157 } else {
158 $hadpass = 0;
160 $rootpass = $password;
161 make_config($rootpass);
162 last if do_query("");
164 die "Unable to connect to the server as root user, giving up.\n"
165 if --$attempts == 0;
167 print "OK, successfully used password, moving on...\n\n";
170 sub set_root_password {
171 my $password1;
172 for (;;) {
173 $password1 = read_without_echo("New password: ");
175 if ( !$password1 ) {
176 print "Sorry, you can't use an empty password here.\n\n";
177 next;
180 my $password2 = read_without_echo("Re-enter new password: ");
182 if ( $password1 ne $password2 ) {
183 print "Sorry, passwords do not match.\n\n";
184 next;
187 last;
190 my $esc_pass = basic_single_escape($password1);
191 do_query("UPDATE mysql.user SET Password=PASSWORD('$esc_pass') WHERE User='root';")
192 or die "Password update failed!\n";
194 print "Password updated successfully!\n";
195 print "Reloading privilege tables..\n";
196 reload_privilege_tables()
197 or die "Can not continue.\n";
199 print "\n";
200 $rootpass = $password1;
201 make_config($rootpass);
204 sub remove_anonymous_users {
205 do_query("DELETE FROM mysql.user WHERE User='';")
206 or die print " ... Failed!\n";
207 print " ... Success!\n";
210 sub remove_remote_root {
211 if (do_query("DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');")) {
212 print " ... Success!\n";
213 } else {
214 print " ... Failed!\n";
218 sub remove_test_database {
219 print " - Dropping test database...\n";
220 if (do_query("DROP DATABASE test;")) {
221 print " ... Success!\n";
222 } else {
223 print " ... Failed! Not critical, keep moving...\n";
226 print " - Removing privileges on test database...\n";
227 if (do_query("DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'")) {
228 print " ... Success!\n";
229 } else {
230 print " ... Failed! Not critical, keep moving...\n";
234 sub reload_privilege_tables {
235 if (do_query("FLUSH PRIVILEGES;")) {
236 print " ... Success!\n";
237 return 1;
238 } else {
239 print " ... Failed!\n";
240 return undef;
244 sub cleanup {
245 print "Cleaning up...\n";
247 foreach my $file ($config, $command) {
248 unlink $file or warn "Warning: Could not unlink $file: $!\n";
253 # The actual script starts here
255 prepare();
257 print <<HERE;
261 NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
262 SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
264 In order to log into MySQL to secure it, we'll need the current
265 password for the root user. If you've just installed MySQL, and
266 you haven't set the root password yet, the password will be blank,
267 so you should just press enter here.
269 HERE
271 get_root_password();
275 # Set the root password
278 print "Setting the root password ensures that nobody can log into the MySQL\n";
279 print "root user without the proper authorisation.\n\n";
281 if ( $hadpass == 0 ) {
282 print "Set root password? [Y/n] ";
283 } else {
284 print "You already have a root password set, so you can safely answer 'n'.\n\n";
285 print "Change the root password? [Y/n] ";
288 my $reply = <STDIN>;
289 if ( $reply =~ /n/i ) {
290 print " ... skipping.\n";
291 } else {
292 set_root_password();
294 print "\n";
298 # Remove anonymous users
301 print <<HERE;
302 By default, a MySQL installation has an anonymous user, allowing anyone
303 to log into MySQL without having to have a user account created for
304 them. This is intended only for testing, and to make the installation
305 go a bit smoother. You should remove them before moving into a
306 production environment.
308 HERE
310 print "Remove anonymous users? [Y/n] ";
311 $reply = <STDIN>;
312 if ( $reply =~ /n/i ) {
313 print " ... skipping.\n";
314 } else {
315 remove_anonymous_users();
317 print "\n";
321 # Disallow remote root login
324 print <<HERE;
325 Normally, root should only be allowed to connect from 'localhost'. This
326 ensures that someone cannot guess at the root password from the network.
328 HERE
330 print "Disallow root login remotely? [Y/n] ";
331 $reply = <STDIN>;
332 if ( $reply =~ /n/i ) {
333 print " ... skipping.\n";
334 } else {
335 remove_remote_root();
337 print "\n";
341 # Remove test database
344 print <<HERE;
345 By default, MySQL comes with a database named 'test' that anyone can
346 access. This is also intended only for testing, and should be removed
347 before moving into a production environment.
349 HERE
351 print "Remove test database and access to it? [Y/n] ";
352 $reply = <STDIN>;
353 if ( $reply =~ /n/i ) {
354 print " ... skipping.\n";
355 } else {
356 remove_test_database();
358 print "\n";
362 # Reload privilege tables
365 print <<HERE;
366 Reloading the privilege tables will ensure that all changes made so far
367 will take effect immediately.
369 HERE
371 print "Reload privilege tables now? [Y/n] ";
372 $reply = <STDIN>;
373 if ( $reply =~ /n/i ) {
374 print " ... skipping.\n";
375 } else {
376 reload_privilege_tables();
378 print "\n";
380 print <<HERE;
384 All done! If you've completed all of the above steps, your MySQL
385 installation should now be secure.
387 Thanks for using MySQL!
390 HERE