#!/usr/bin/perl # # by Jakub Wartak 17.02.2006 # Licensed under GPLv2 # # requires root mysql privileges # ver 0.0.2 # TODO: MySQL-5.x support, functions checks ######################################################################################### use DBI; use warnings; use strict; use Fcntl ':mode'; # location of my.cnf my @locini = ( "/etc/debian/my.cnf", "/etc/my.cnf", "/var/db/mysql/my.cnf", "/var/lib/mysql/my.cnf", ); my ($db, $mysql); $| = 1; print "Enter MySQL admin login [root]: "; my $sql_login = ; chomp $sql_login if ($sql_login); $sql_login = "root" if(!$sql_login || $sql_login eq ''); print "Enter MySQL passwd for $sql_login: "; my $sql_pw = ; chomp $sql_pw if($sql_pw); $sql_pw = "" if(!$sql_pw || $sql_pw eq ''); print "Enter MySQL host (ip or hostname) [localhost]: "; my $sql_server = ; chomp $sql_server if($sql_server); $sql_server = 'localhost' if(!$sql_server || $sql_server eq ''); print "\n"; print "Connecting to $sql_login\@$sql_server..."; $db = DBI->connect("DBI:mysql::$sql_server", $sql_login, $sql_pw) or die $DBI::errstr; $mysql = DBI->connect("DBI:mysql:mysql:$sql_server", $sql_login, $sql_pw) or die $DBI::errstr; print "connected!\n\n"; $| = 0; ### DB table checks ### my $dbtab = $mysql->prepare("SELECT Host, Db, User, Grant_Priv FROM db") or die $mysql->errstr; $dbtab->execute or die $mysql->errstr; while ( my ($db_host, $db_db, $db_user, $grant ) = $dbtab->fetchrow_array) { my $login = $db_user . '\@' . $db_host; print "WARNING: $login has GRANT defined on $db_db !!\n" if ($grant eq 'Y'); } $dbtab->finish; ### USER table checks ### my $user = $mysql->prepare("SELECT Host, User, Password, Grant_Priv, Reload_Priv, Shutdown_Priv, Process_Priv, Super_Priv FROM user") or die $mysql->errstr; $user->execute or die $mysql->errstr; while ( my ($db_host, $db_user, $db_pw, $grant, $reload, $shutdown, $process, $super ) = $user->fetchrow_array) { my $login = $db_user . '@' . $db_host; print "WARNING: ANONYMOUS login $login\n" if $db_user eq ''; print "WARNING: REMOTE ROOT $login !!\n" if ($db_user eq 'root' && $db_host ne 'localhost'); print "WARNING: Empty password for $login !!\n" if ($db_pw eq ''); print "WARNING: $login can give GRANTs\n" if ($grant eq 'Y' and $db_user ne 'root'); print "NOTICE: $login can reload/shutdown MySQL\n" if ($reload eq 'Y' || $shutdown eq 'Y'); print "NOTICE: $login can use 'SHOW PROCESSLIST'\n" if ($process eq 'Y'); print "NOTICE: $login is an administrative account ( has Super privilege )\n" if ($super eq 'Y'); } $user->finish; my $vars = $mysql->prepare("SHOW VARIABLES") or die $mysql->errstr; $vars->execute or die $mysql->errstr; while ( my ($name, $value) = $vars->fetchrow_array) { if($name eq 'socket') { my $sock = $value; # TODO ( perms ) } if($name eq 'local_infile' && $value eq 'OFF') { print "WARNING: You could disable LOCAL INFILE in my.cnf to improve security\n"; } if($name eq 'skip_show_database' && $value eq 'NO') { print "NOTICE: You could enable 'skip_show_database' in my.cnf to improve security\n"; } if($name eq 'old_passwords' && $value eq 'YES') { print "NOTICE: Old format (MySQL-4.0) of passwords is used\n"; } if($name eq 'pid_file' && -f $value) { # TODO: check for world writeable print "WARNING: Pid file ($value) is readable by others\n" if(&getperm($value) & S_IROTH); print "WARNING: Pid file ($value) is writeable by others\n" if(&getperm($value) & S_IWOTH); } # should be 0770 if($name eq 'datadir' && -d $value) { print "WARNING: Datadir ($value) is readable by others\n" if(&getperm($value) & S_IROTH); print "WARNING: Datadir ($value) is writeable by others\n" if(&getperm($value) & S_IWOTH); print "WARNING: Datadir ($value) can be accessed by others\n" if(&getperm($value) & S_IXOTH); } if($name eq 'character_sets_dir' && -d $value ) { # TODO } # IMHO: this dir should be separated from /tmp if($name eq 'tmpdir' && -d $value ) { print "WARNING: Tmpdir for MySQL ($value) is readable by others\n" if(&getperm($value) & S_IROTH); print "WARNING: Tmpdir for MySQL ($value) is writeable by others\n" if(&getperm($value) & S_IWOTH); print "WARNING: Tmpdir for MySQL ($value) can be accessed by others\n" if(&getperm($value) & S_IXOTH); } } # db will be used in future to check/enumerate non-'mysql' specific databases # eg. 'is accesible' => don't have broken InnoDB/ARCHIVE tables etc. $db->disconnect(); $mysql->disconnect(); exit 0; sub getperm { my $d = shift; return (stat($d))[2]; }