2
# ****************************
5
use File::Temp qw(tempfile tmpnam);
9
# ****************************
10
# static information...
11
$VERSION = "2.06, 20 Dec 2000";
14
$script = 'MySQLAccess' unless $script;
15
$script_conf = "$script.conf";
16
$script_log = $ENV{'HOME'}."/$script.log";
18
# ****************************
19
# information on MySQL
20
$MYSQL = '@bindir@/mysql'; # path to mysql executable
22
$MYSQL_OPT = ' --batch --unbuffered';
23
$ACCESS_DB = 'mysql'; # name of DB with grant-tables
28
$ACCESS_H_TMP = 'host_tmp';
29
$ACCESS_U_TMP = 'user_tmp';
30
$ACCESS_D_TMP = 'db_tmp';
31
$ACCESS_H_BCK = 'host_backup';
32
$ACCESS_U_BCK = 'user_backup';
33
$ACCESS_D_BCK = 'db_backup';
34
$DIFF = '/usr/bin/diff';
35
$MYSQLDUMP = '@bindir@/mysqldump';
36
#path to mysqldump executable
38
$MYSQLADMIN= 'http://foobar.com/MySQLadmin';
39
#URL of CGI for manipulating
40
#the temporary grant-tables
44
unlink $MYSQL_CNF if defined $MYSQL_CNF and not $DEBUG;
48
--------------------------------------------------------------------------
49
mysqlaccess (Version $VERSION)
51
Copyright (C) 1997,1998 Yves.Carlier\@rug.ac.be
52
University of Ghent (RUG), Belgium
53
Administratieve Informatieverwerking (AIV)
55
report the access-privileges for a USER from a HOST to a DB
57
Many thanks go to <monty\@mysql.com> and <psmith\@BayNetworks.COM>
58
for their suggestions, debugging and patches.
60
use `$script -?' to get more information on available options.
62
From version 2.0x, $script can also be used through a WEB-browser
63
if it is ran as a CGI-script. (See the release-notes)
65
--------------------------------------------------------------------------
68
$OPTIONS = <<_OPTIONS;
70
Usage: $script [host [user [db]]] OPTIONS
72
-?, --help display this helpscreen and exit
73
-v, --version print information on the program `$script'
75
-u, --user=# username for logging in to the db
76
-p, --password=# validate password for user
77
-h, --host=# name or IP-number of the host
78
-d, --db=# name of the database
80
-U, --superuser=# connect as superuser
81
-P, --spassword=# password for superuser
82
-H, --rhost=# remote MySQL-server to connect to
83
--old_server connect to old MySQL-server (before v3.21) which
84
does not yet know how to handle full where clauses.
86
-b, --brief single-line tabular report
87
-t, --table report in table-format
89
--relnotes print release-notes
90
--plan print suggestions/ideas for future releases
91
--howto some examples of how to run `$script'
92
--debug=N enter debuglevel N (0..3)
94
--copy reload temporary grant-tables from original ones
95
--preview show differences in privileges after making
96
changes in (temporary) grant-tables
97
--commit copy grant-rules from temporary tables to grant-tables
98
(!don't forget to do an mysqladmin reload)
99
--rollback undo the last changes to the grant-tables.
102
+ At least the user and the db must be given (even with wildcards)
103
+ If no host is given, `localhost' is assumed
104
+ Wilcards (*,?,%,_) are allowed for host, user and db, but be sure
105
to escape them from your shell!! (ie type \\* or '*')
108
$RELEASE = <<'_RELEASE';
115
0.1-beta2: (1997-02-27)
116
- complete rewrite of the granting-rules, based on the documentation
118
- IP-number and name for a host are equiv.
120
0.1-beta3: (1997-03-10)
122
- 'localhost' and the name/ip of the local machine are now equiv.
124
0.1-beta4: (1997-03-11)
125
- inform the user if he has not enough priv. to read the mysql db
127
1.0-beta1: (1997-03-12)
128
suggestions by Monty:
129
- connect as superuser with superpassword.
130
- mysqlaccess could also notice if all tables are empty. This means
131
that all user have full access!
132
- It would be nice if one could optionally start mysqlaccess without
133
any options just the arguments 'user db' or 'host user db', where
134
host is 'localhost' if one uses only two arguments.
136
1.0-beta2: (1997-03-14)
137
- bugfix: translation to reg.expr of \_ and \%.
138
- bugfix: error in matching regular expression and string given
139
by user which resulted in
140
'test_123' being matched with 'test'
142
1.0-beta3: (1997-03-14)
143
- bugfix: the user-field should not be treated as a sql-regexpr,
144
but as a plain string.
145
- bugfix: the host-table should not be used if the host isn't empty in db
146
or if the host isn't emty in user
149
1.0-beta4: (1997-03-14)
150
- bugfix: in an expression "$i = $j or $k", the '=' binds tighter than the or
151
which results in problems...
153
- running mysqlaccess with "perl -w" gives less warnings... ;-)
155
1.0-beta5: (1997-04-04)
156
- bugfix: The table sorting was only being applied to the "user" table; all
157
the tables need to be sorted. Rewrote the sort algorithm, and
158
the table walk algorithm (no temp file anymore), and various
159
other cleanups. I believe the access calculation is 100% correct.
160
(by Paul D. Smith <psmith\@baynetworks.com>)
161
- Allow the debug level to be set on the cmd line with --debug=N.
162
(by Paul D. Smith <psmith\@baynetworks.com>)
163
- More -w cleanups; should be totally -w-clean.
164
(by Paul D. Smith <psmith\@baynetworks.com>)
166
1.1-beta1: (1997-04-xx)
167
1.1-beta2: (1997-04-11)
169
--all_users : report access-rights for all possible users
170
--all_dbs : report access-rights for all possible dbs
171
--all_hosts : report access-rights for all possible hosts
172
--brief : as brief as possible, don't mention notes,warnings and rules
173
--password : validate password for user
174
- layout: long messages are wrapped on the report.
176
more descriptive notes and warnings
177
wildcards (*,?) are allowed in the user,host and db options
178
setting xxxx=* is equiv to using option --all_xxxx
179
note: make sure you escape your wildcards, so they don't get
180
interpreted by the shell. use \* or '*'
181
- bugfix: Fieldnames which should be skipped on the output can now have
182
a first capital letter.
183
- bugfix: any option with a '.' (eg ip-number) was interpreted as
184
a wildcard-expression.
185
- bugfix: When no entry was found in the db-table, the default accessrights are
186
N, instead of the faulty Y in a previous version.
188
1.1-beta-3 : (1997-04-xx)
189
1.1-beta-4 : (1997-04-xx)
190
1.1-beta-5 : (1997-04-xx)
193
--rhost : name of mysql-server to connect to
194
--plan : print suggestions/ideas for future releases
195
--relnotes : display release-notes
196
--howto : display examples on how to use mysqlaccess
197
--brief : single-line tabular output
198
- functionality/bugfix:
199
* removed options --all_users,--all_dbs,--all_hosts, which
200
were redundant with the wildcard-expressions for the corresponding
201
options. They made the processing of the commandline too painful
203
(suggested by psmith)
204
* redefined the option --brief, which now gives a single-line
206
* Now we check if the right version of the mysql-client is used,
207
since we might use an option not yet implemented in an
208
older version (--unbuffered, since 3.0.18)
209
Also the error-messages the mysql-client reports are
210
better interpreted ;-)
211
* Wildcards can now be given following the SQL-expression
212
(%,_) and the Regular-expression (*,?) syntax.
213
- speed: we now open a bidirectional pipe to the mysql-client, and keep
214
it open throughout the whole run. Queries are written to,
215
and the answers read from the pipe.
218
* the Rules were not properly reset over iterations
219
* when in different tables the field-names were not identical,
220
eg. Select_priv and select_priv, they were considered as
221
definitions of 2 different access-rights.
222
* the IP-number of a host with a name containing wildcards should
223
not be searched for in Name2IP and IP2Name.
224
* various other small things, pointed out by <monty> and <psmith>
228
* Fixed bug in acl with anonymous user: Now if one gets accepted by the
229
user table as a empty user name, the user name is set to '' when
230
checking against the 'db' and 'host' tables. (Bug fixed in MySQL3.20.19)
234
* hashes should be initialized with () instead of {} <psmith>
235
* "my" variable $name masks earlier declaration in same scope,
236
using perl 5.004 <????>
240
2.0p1-3 : (1997-10-xx)
243
* log-file for debug-output : /tmp/mysqlaccess.log
244
* default values are read from a configuration file $script.conf
245
first this file is looked for in the current directory; if not
246
found it is looked for in /etc/
247
Note that when default-values are given, these can't get overriden
248
by empty (blanc) values!
249
* CGI-BIN version with HTML and forms interface. Simply place the
250
script in an ScriptAliased directory, make the configuration file
251
available in the that directory or in /etc, and point your browser
253
* copy the grant-rules to temporary tables, where you are safe to
255
* preview changes in privileges after changing grant-rules,
256
before taking them into production
257
* copy the new grant-rules from the temporary tables back to the
259
* Undo all changes made in the grant-tables (1-level undo).
261
* --table : as opposite of the --brief option.
262
* --copy : (re)load temporary grant-tables from original ones.
263
* --preview : preview changes in privileges after changing
264
some or more entries in the grant-tables.
265
* --commit : copy grant-rules from temporary tables to grant-tables
266
(!don't forget to do an mysqladmin reload)
267
* --rollback: undo the last changes to the grant-tables.
270
* if the table db is empty, mysqlaccess freezed
271
(by X Zhu <X.Zhu@Bradford.ac.uk>)
274
- fixed some "-w" warnings.
275
- complain when certain programs and paths can't be found.
279
* rules for db-table where not calculated and reported correctly.
282
* Privileges of the user-table were not AND-ed properly with the
283
other privileges. (reported by monty)
285
* --old_server: mysqlaccess will now use a full where clause when
286
retrieving information from the MySQL-server. If
287
you are connecting to an old server (before v3.21)
288
then use the option --old_server.
291
* in Host::MatchTemplate: incorrect match if host-field was left empty.
293
2.04-alpha1 : (2000-02-11)
294
Closes vulnerability due to former implementation requiring passwords
295
to be passed on the command line.
297
Option values for --password -p -spassword -P may now be omitted from
298
command line, in which case the values will be prompted for.
299
(fix supplied by Steve Harvey <sgh@vex.net>)
301
2.05: (2000-02-17) Monty
302
Moved the log file from /tmp to ~
304
2.06: Don't print '+++USING FULL WHERE CLAUSE+++'
312
-a full where clause is use now. How can we handle older servers?
313
-add some more functionality for DNS.
314
-select the warnings more carefuly.
315
>> I think that the warnings should either be enhanced to _really_
316
>> understand and report real problems accurately, or restricted to
317
>> only printing things that it knows with 100% certainty. <psmith)
318
>> Why do I have both '%' and 'any_other_host' in there? Isn't that
319
>> the same thing? I think it's because I have an actual host '%' in
320
>> one of my tables. Probably the script should catch that and not
321
>> duplicate output. <psmith>
325
# From the FAQ: the Grant-algorithm
326
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
327
# The host table is mainly to maintain a list of "secure" servers.
328
# At TCX hosts contain a list of all machines on local network. These are granted
330
# Technically the user grant is calculated by:
332
# 1.First sort all entries by host by putting host without wildcards first,
333
# after this host with wildcards and entries with host = ".
334
# Under each host sort user by the same criterias.
335
# 2.Get grant for user from the "db" table.
336
# 3.If hostname is "empty" for the found entry, AND the privileges with
337
# the privileges for the host in "host" table.
338
# (Remove all which is not "Y" in both)
339
# 4.OR (add) the privileges for the user from the "user" table.
340
# (add all privileges which is "Y" in "user")
342
# When matching, use the first found match.
344
# -----------------------------------------------------------------------------------
348
Examples of how to call $script:
350
1)Calling $script with 2 arguments:
352
\$ $script root mysql
353
->report rights of user root logged on at the local host in db mysql
356
for USER 'root', from HOST 'localhost', to DB 'mysql'
357
+-----------------+---+ +-----------------+---+
358
| select_priv | Y | | drop_priv | Y |
359
| insert_priv | Y | | reload_priv | Y |
360
| update_priv | Y | | shutdown_priv | Y |
361
| delete_priv | Y | | process_priv | Y |
362
| create_priv | Y | | file_priv | Y |
363
+-----------------+---+ +-----------------+---+
364
BEWARE: Everybody can access your DB as user 'root'
365
: WITHOUT supplying a password. Be very careful about it!!
367
The following rules are used:
368
db : 'No matching rule'
369
host : 'Not processed: host-field is not empty in db-table.'
370
user : 'localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y'
372
2)Calling $script with 3 arguments:
374
\$ $script foo.bar nobody Foo
375
->report rights of user root logged in at machine foobar to db Foo
378
for USER 'nobody', from HOST 'foo.bar', to DB 'Foo'
379
+-----------------+---+ +-----------------+---+
380
| select_priv | Y | | drop_priv | N |
381
| insert_priv | Y | | reload_priv | N |
382
| update_priv | Y | | shutdown_priv | N |
383
| delete_priv | Y | | process_priv | N |
384
| create_priv | N | | file_priv | N |
385
+-----------------+---+ +-----------------+---+
386
BEWARE: Everybody can access your DB as user 'nobody'
387
: WITHOUT supplying a password. Be very careful about it!!
389
The following rules are used:
390
db : 'foo.bar','Foo','nobody','Y','Y','Y','N','N','N'
391
host : 'Not processed: host-field is not empty in db-table.'
392
user : 'foo.bar','nobody','','N','N','N','Y','N','N','N','N','N','N'
396
\$ $script \\* nobody Foo --brief
397
->report access-rights of user nobody from all machines to db Foo,
398
and use a matrix-report.
400
Sel Ins Upd Del Crea Drop Reld Shut Proc File Host,User,DB
401
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --------------------
402
Y Y Y Y N N N N N N localhost,nobody,Foo
403
N N N N N N N N N N %,nobody,Foo
404
N N N N N N N N N N any_other_host,nobody,Foo
409
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
410
# START OF THE PROGRAM #
411
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
418
# ****************************
420
# can be set to 0,1,2,3
421
# a higher value gives more info
422
# ! this can also be set on the command-line
425
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>8
426
# Normaly nothing should be changed beneeth this line
429
# ****************************
430
# no caching on STDOUT
433
$MYSQL_CNF = tmpnam();
434
%MYSQL_CNF = (client => { },
441
$NEW_USER = 'ANY_NEW_USER';
442
$NEW_DB = 'ANY_NEW_DB' ;
445
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #
449
# and start the program by processing the parameters #
450
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #
452
($CMD,$CGI) = GetMode();
454
# ****************************
455
# the copyright message should
456
# always be printed (once)
457
MySQLaccess::Report::Print_Header();
459
# *****************************
460
# Read configuration-file
461
MySQLaccess::Debug::Print(1, "Reading configuration file...");
462
if (-f "./$script_conf") {
463
require "./$script_conf";
465
elsif (-f "@sysconfdir@/$script_conf") {
466
require "@sysconfdir@/$script_conf";
468
elsif (-f "/etc/$script_conf") {
469
require "/etc/$script_conf";
472
# ****************************
473
# Read in all parameters
474
if ($MySQLaccess::CMD) { #command-line version
475
# ----------------------------
476
# Get options from commandline
477
$Getopt::Long::ignorecase=0; #case sensitive options
478
if ( grep(/\-\?/,@ARGV) ) { MySQLaccess::Report::Print_Usage(); exit 0; }
479
GetOptions("help" => \$Param{'help'}
480
,"host|h=s" => \$Param{'host'}
481
,"user|u=s" => \$Param{'user'}
482
,"password|p:s" => \$Param{'password'}
483
,"db|d=s" => \$Param{'db'}
484
,"superuser|U=s" => \$Param{'superuser'}
485
,"spassword|P:s" => \$Param{'spassword'}
486
,"rhost|H=s" => \$Param{'rhost'}
487
,"old_server" => \$Param{'old_server'}
488
,"debug=i" => \$Param{'DEBUG'}
489
,"brief|b" => \$Param{'brief'}
490
,"table|t" => \$Param{'table'}
491
,"relnotes" => \$Param{'relnotes'}
492
,"plan" => \$Param{'plan'}
493
,"howto" => \$Param{'howto'}
494
,"version|v" => \$Param{'version'}
495
,"preview" => \$Param{'preview'}
496
,"copy" => \$Param{'copy'}
497
,"commit" => \$Param{'commit'}
498
,'rollback' => \$Param{'rollback'}
501
# -----------------------------
503
$DEBUG = $Param{'DEBUG'} if ($Param{'DEBUG'}>=$DEBUG);
505
# -----------------------------
506
# check for things which aren't
507
# declared as options:
508
# 2 arguments: (user,db) -> ('localhost','user','db')
510
MySQLaccess::Debug::Print(2,"$script called with 2 arguments:");
511
$Param{'host'} = $Param{'host'} || 'localhost';
512
$Param{'user'} = $ARGV[0] || $Param{'user'};
513
$Param{'db'} = $ARGV[1] || $Param{'db'};
515
# 3 arguments: (host,user,db)
517
MySQLaccess::Debug::Print(2,"$script called with 3 arguments:");
518
$Param{'host'} = $ARGV[0] || $Param{'host'};
519
$Param{'user'} = $ARGV[1] || $Param{'user'};
520
$Param{'db'} = $ARGV[2] || $Param{'db'};
523
# -------------------------------------
524
# prompt for user password if requested
525
if ( defined($Param{'password'}) && length($Param{'password'}) == 0 ) {
526
$Param{'password'} = PromptPass(
527
"Password for MySQL user $Param{'user'}: ");
530
if ($MySQLaccess::CGI) { #CGI-version
533
$Param{'help'} = $Q->param('help') ;
534
$Param{'host'} = $Q->param('host') || $Q->param('h') || $Param{'host'};
535
$Param{'user'} = $Q->param('user') || $Q->param('u') || $Param{'user'};
536
$Param{'db'} = $Q->param('db') || $Q->param('d') || $Param{'db'};
537
$Param{'password'} = $Q->param('password') || $Q->param('p') || $Param{'password'};
538
$Param{'superuser'} = $Q->param('superuser') || $Q->param('U') || $Param{'superuser'};
539
$Param{'spassword'} = $Q->param('spassword') || $Q->param('P') || $Param{'spassword'};
540
$Param{'rhost'} = $Q->param('rhost') || $Q->param('H') || $Param{'rhost'};
541
$Param{'old_server'}= $Q->param('old_server')|| $Param{'old_server'};
542
$Param{'debug'} = $Q->param('debug') || $Param{'debug'};
543
$Param{'brief'} = $Q->param('brief') || $Param{'brief'};
544
$Param{'table'} = $Q->param('table') || $Param{'table'};
545
$Param{'relnotes'} = $Q->param('relnotes');
546
$Param{'plan'} = $Q->param('plan');
547
$Param{'howto'} = $Q->param('howto');
548
$Param{'version'} = $Q->param('version') ? $Q->param('version') : $Q->param('v');
549
$Param{'edit'} = $Q->param('edit');
550
$Param{'preview'} = $Q->param('preview');
551
$Param{'copy'} = $Q->param('copy');
552
$Param{'commit'} = $Q->param('commit');
553
$Param{'rollback'} = $Q->param('rollback');
554
# -----------------------------
556
$DEBUG = $Q->param('debug') if ($Q->param('debug')>=$DEBUG);
559
# ----------------------
560
# brief and table-format
562
# table-format is prefered
563
if (defined($Param{'table'})) { undef($Param{'brief'}); }
564
if (defined($Param{'preview'}) or
565
defined($Param{'copy'}) or
566
defined($Param{'commit'}) or
567
defined($Param{'rollback'}) ) { $Param{'edit'}='on'; }
570
# ----------------------
571
# if no host is given
572
# assume we mean 'localhost'
573
if (!defined($Param{'host'})) { $Param{'host'}='localhost'; }
575
# ----------------------
576
# perform some checks
577
# -> eliminate 'broken pipe' error
578
push(@MySQLaccess::Grant::Error,'not_found_mysql') if !(-x $MYSQL);
579
push(@MySQLaccess::Grant::Error,'not_found_diff') if !(-x $DIFF);
580
push(@MySQLaccess::Grant::Error,'not_found_mysqldump') if !(-x $MYSQLDUMP);
581
if (@MySQLaccess::Grant::Error) {
582
MySQLaccess::Report::Print_Error_Messages() ;
586
#-----------------------
587
# get info/help if necc.
589
if ( defined($Param{'version'}) ) {
590
MySQLaccess::Report::Print_Version();
592
MySQLaccess::Report::Print_Footer();
593
MySQLaccess::DB::CloseConnection();
597
if ( defined($Param{'relnotes'}) ) {
598
MySQLaccess::Report::Print_Relnotes();
600
MySQLaccess::Report::Print_Footer();
601
MySQLaccess::DB::CloseConnection();
605
if ( defined($Param{'plan'}) ) {
606
MySQLaccess::Report::Print_Plans();
608
MySQLaccess::Report::Print_Footer();
609
MySQLaccess::DB::CloseConnection();
613
if ( defined($Param{'howto'}) ) {
614
MySQLaccess::Report::Print_HowTo();
616
MySQLaccess::Report::Print_Footer();
617
MySQLaccess::DB::CloseConnection();
622
# -----------------------------
623
# generate a help-screen in CMD-mode
624
# or a blanc form in CGI-mode
625
if ( defined($Param{'help'})
626
or !defined($Param{'user'})
627
or !defined($Param{'host'})
628
or !defined($Param{'db'})
630
push(@MySQLaccess::Grant::Error,'user_required') unless defined($Param{'user'});
631
push(@MySQLaccess::Grant::Error,'db_required') unless defined($Param{'db'});
632
push(@MySQLaccess::Grant::Error,'host_required') unless defined($Param{'host'});
633
MySQLaccess::Report::Print_Usage() if $print_usage;
638
# ----------------------------
639
# get hostname and local-ip
641
$localhost = MySQLaccess::Host::LocalHost();
642
$local_ip = MySQLaccess::Host::Name2IP($localhost);
643
$MySQLaccess::Host::localhost = MySQLaccess::Host::LocalHost();
644
$MySQLaccess::Host::local_ip = MySQLaccess::Host::Name2IP($localhost);
645
MySQLaccess::Debug::Print(3, "localhost name=$localhost, ip=$local_ip");
647
#-----------------------------------
648
# version of MySQL-server to connect
649
# to determine use of full where clause
650
$MySQLaccess::Host::SERVER = $Param{'old_server'} ? '3.20' : $SERVER;
652
#---------------------------------
653
# create the config file for mysql and mysqldump
654
# to avoid passing authentication info on the command line
657
die "Unsafe config file found: $unsafeConfig\n" if $unsafeConfig;
658
if (defined($Param{'superuser'})) {
659
$MYSQL_CNF{'mysql'}{'user'} = $Param{'superuser'};
660
$MYSQL_CNF{'mysqldump'}{'user'} = $Param{'superuser'};
662
if (defined($Param{'spassword'})) {
663
if ( $CMD && length($Param{'spassword'}) == 0 ) {
664
$Param{'spassword'} =
665
PromptPass("Password for MySQL superuser $Param{'superuser'}: ");
667
if ( length($Param{'spassword'}) > 0 ) {
668
$MYSQL_CNF{'mysql'}{'password'} = $Param{'spassword'};
669
$MYSQL_CNF{'mysqldump'}{'password'} = $Param{'spassword'};
672
WriteTempConfigFile();
674
#---------------------------------
675
# Inform user if he has not enough
676
# privileges to read the access-db
677
if ( $nerror=MySQLaccess::DB::OpenConnection() ) {
678
MySQLaccess::Report::Print_Error_Access($nerror);
682
# -----------------------
683
# Read MySQL ACL-files
684
if ($nerror=MySQLaccess::Grant::ReadTables()) {
685
MySQLaccess::Report::Print_Error_Access($nerror);
688
if ($Param{'edit'} and $nerror=MySQLaccess::Grant::ReadTables('tmp')) {
689
MySQLaccess::Report::Print_Error_Access($nerror);
693
#---------------------------------
694
# reload temporay grant-tables
695
# with data from original ones
696
if ( defined($Param{'copy'}) ) {
697
$nerror=MySQLaccess::DB::LoadTmpTables();
699
MySQLaccess::Report::Print_Error_Access($nerror);
702
my $msg = "The grant-rules are copied from the grant-tables to\n"
703
. "the temporary tables.";
704
MySQLaccess::Report::Print_Message([$msg]);
705
# MySQLaccess::Report::Print_Footer();
706
# MySQLaccess::DB::CloseConnection();
711
#---------------------------------
712
# preview result of changes in the
714
if ( defined($Param{'preview'}) ) {
715
$aref=MySQLaccess::Grant::Diff_Privileges();
716
MySQLaccess::Report::Print_Diff_ACL($aref);
717
# MySQLaccess::Report::Print_Footer();
718
# MySQLaccess::DB::CloseConnection();
723
#---------------------------------
724
# reload grant-tables
725
# with data from temporary tables
726
if ( defined($Param{'commit'}) ) {
727
if ($nerror = MySQLaccess::DB::CommitGrantTables()) {
728
MySQLaccess::Report::Print_Error_Access($nerror);
731
my $msg = "The grant-rules have been copied from the temporary tables\n"
732
. "to the grant-tables.";
733
my $msg1= "Don't forget to do an 'mysqladmin reload' before these\n"
734
. "changes take effect.";
735
my $msg2= "A backup-version of your original grant-rules are saved in the\n"
736
. "backup-tables, so you can always perform a 1-level rollback.";
737
MySQLaccess::Report::Print_Message([$msg,$msg1,$msg2]);
738
# MySQLaccess::Report::Print_Footer();
739
# MySQLaccess::DB::CloseConnection();
743
#---------------------------------
744
# restore previous grant-rules
745
# with data from backup tables
746
if ( defined($Param{'rollback'}) ) {
747
if ($nerror = MySQLaccess::DB::RollbackGrantTables()) {
748
MySQLaccess::Report::Print_Error_Access($nerror);
751
my $msg = "The old grant-rules have been copied back from the backup tables\n"
752
. "to the grant-tables.";
753
my $msg1= "Don't forget to do an 'mysqladmin reload' before these\n"
754
. "changes take effect.";
755
MySQLaccess::Report::Print_Message([$msg,$msg1]);
756
# MySQLaccess::Report::Print_Footer();
757
# MySQLaccess::DB::CloseConnection();
760
#----------------------------------
762
if ( defined($Param{'edit'})) {
763
if ($MySQLaccess::CGI ) {
764
MySQLaccess::Report::Print_Edit();
766
MySQLaccess::Report::Print_Footer();
767
MySQLaccess::DB::CloseConnection();
771
MySQLaccess::Report::Print_Edit();
773
MySQLaccess::Report::Print_Footer();
774
MySQLaccess::DB::CloseConnection();
780
# -----------------------------
781
# Build list of users,dbs,hosts
783
@all_dbs = @{MySQLaccess::DB::Get_All_dbs($Param{'db'})};
784
@all_users = @{MySQLaccess::DB::Get_All_users($Param{'user'})};
785
@all_hosts = @{MySQLaccess::DB::Get_All_hosts($Param{'host'})};
787
#@all_dbs_tmp = @{MySQLaccess::DB::Get_All_dbs($Param{'db'},'tmp')};
788
#@all_users_tmp = @{MySQLaccess::DB::Get_All_users($Param{'user'},'tmp')};
789
#@all_hosts_tmp = @{MySQLaccess::DB::Get_All_hosts($Param{'host'},'tmp')};
791
# -----------------------------
792
# Report access-rights for each
793
# tuple (host,user,db)
796
foreach $host (@all_hosts) {
797
foreach $user (@all_users) {
798
foreach $db (@all_dbs) {
799
MySQLaccess::Grant::Initialize();
800
%Access = MySQLaccess::Grant::Get_Access_Rights($host,$user,$db);
801
MySQLaccess::Report::Print_Access_rights($host,$user,$db,\%Access);
806
# -----------------------------
808
MySQLaccess::Report::Print_Footer();
809
MySQLaccess::DB::CloseConnection();
812
#############################################################
818
if (defined($ENV{'HTTP_HOST'})) { $cmd=0; $cgi=1; }
819
else { $cmd=1; $cgi=0; }
823
# ================================
825
# prompt tty for a password
826
# ================================
830
$ENV{PATH} = "/bin:/usr/bin";
832
$ENV{SHELL} = "/bin/sh";
835
chomp($password = <STDIN>);
841
# =================================
842
# sub CheckUnsafeFile
843
# tell if a config file containing a password is unsafe
844
# =================================
845
sub CheckUnsafeFile {
847
my ($dev, $ino, $mode, $nlink,
848
$uid, $gid, $rdev, $size,
849
$atime, $mtime, $ctime, $blksize, $blocks) = stat($fname);
851
if ( $uid != $< ) { # unsafe if owned by other than current user
854
if ( $mode & 066 ) { # unsafe if accessible by other
857
$fname =~ s#/[^/]+$##;
858
if ( (length $fname) > 0 ) {
859
return CheckUnsafeDir($fname);
864
# =================================
866
# tell if a directory is unsafe
867
# =================================
870
my ($dev, $ino, $mode, $nlink,
871
$uid, $gid, $rdev, $size,
872
$atime, $mtime, $ctime, $blksize, $blocks) = stat($fname);
874
# not owned by me or root
875
if ( ($uid != $<) && ($uid != 0) ) {
878
if ( $mode & 022 ) { # unsafe if writable by other
879
return 1 unless $mode & 01000; # but sticky bit ok
881
$fname =~ s#/[^/]+$##;
882
if ( (length $fname) > 0 ) {
883
return CheckUnsafeDir($fname);
888
# =================================
889
# sub MergeConfigFile
890
# merge data from .cnf file
891
# =================================
892
sub MergeConfigFile {
894
my ($group, $item, $value);
895
if ( open CNF, $fname ) {
899
if ( /\[\s*(\w+)\s*]/ ) {
901
$group =~ tr/A-Z/a-z/;
902
if ( !exists $MYSQL_CNF{$group} ) {
905
} elsif ( defined $group ) {
906
($item, $value) = /((?:\w|-)+)\s*=\s*(\S+)/;
907
# don't unquote backslashes as we just write it back out
908
if ( defined $item ) {
909
if ( $item =~ /^password$/ ) {
910
if ( CheckUnsafeFile($fname) ) {
911
$unsafeConfig = $fname;
914
if ( $group eq 'client' ) {
915
$MYSQL_CNF{'mysql'}{$item} = $value;
916
$MYSQL_CNF{'mysqldump'}{$item} = $value;
918
$MYSQL_CNF{$group}{$item} = $value;
927
# =================================
928
# sub MergeConfigFiles
929
# merge options from config files
930
# NOTE: really should do two separate merges for each
931
# client to exactly duplicate order of resulting argument lists
932
# =================================
933
sub MergeConfigFiles {
934
my ($name,$pass,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwuid $<;
935
MergeConfigFile("@sysconfdir@/my.cnf");
936
MergeConfigFile("/etc/my.cnf");
937
MergeConfigFile("$dir/.my.cnf");
940
# =================================
941
# sub WriteTempConfigFile
943
# =================================
944
sub WriteTempConfigFile {
945
sysopen CNFFILE, $MYSQL_CNF, O_RDWR|O_CREAT|O_EXCL, 0700
946
or die "sysopen $MYSQL_CNF: $!";
948
# groups may be in any order, generic groups such as [client] assumed
950
foreach $group (keys %MYSQL_CNF) {
951
print CNFFILE "[$group]\n";
952
foreach $item (keys %{$MYSQL_CNF{$group}}) {
953
if ( defined $MYSQL_CNF{$group}{$item} ) {
954
print CNFFILE "$item=$MYSQL_CNF{$group}{$item}\n";
956
print CNFFILE "$item\n";
964
######################################################################
965
package MySQLaccess::DB;
969
$DEBUG = $MySQLaccess::DEBUG unless ($DEBUG);
970
# Error-messages from the MySQL client
971
%ACCESS_ERR= ('Access_denied' => 'Access denied'
972
,'Dbaccess_denied' => 'Access to database denied'
973
,'Unrecognized_option' => 'unrecognized option'
974
,'Unknown_table' => "Can't find file:"
975
,'unknown_error' => '^ERROR:'
978
# ######################################
979
# Connecting to the MYSQL DB
980
# ======================================
982
# Open an connection to the mysql-db
983
# questions to MYSQL_Q
984
# answers from MYSQL_A
985
# ======================================
988
MySQLaccess::Debug::Print(2,"OpenConnection:");
990
# check path to mysql-client executable
991
if (! -f $MySQLaccess::MYSQL) {
992
if ($MySQLaccess::CMD) { die "Could not find MySQL-client '$MySQLaccess::MYSQL'"; }
993
if ($MySQLaccess::CGI) {
994
print "<center>\n<font color=Red>\n";
995
print "ERROR: Could not find MySQL-client '$MySQLaccess::MYSQL'";
996
print "</center>\n</font>\n";
1001
# path to mysql executable
1002
my $connect = "$MySQLaccess::MYSQL --defaults-file=$MySQLaccess::MYSQL_CNF";
1003
$connect .= " $MySQLaccess::MYSQL_OPT";
1004
# superuser, spassword transmitted via defaults-file
1005
if (defined($MySQLaccess::Param{'rhost'})) { $connect .= " --host=$MySQLaccess::Param{'rhost'}"; }
1009
$connect .= " $MySQLaccess::ACCESS_DB";
1011
# open connection (not using /bin/sh -c)
1012
MySQLaccess::Debug::Print(2,"Connecting to: $connect");
1013
$pid=IPC::Open3::open3(\*MYSQL_Q,\*MYSQL_A,"",split /\s+/,$connect);
1014
MySQLaccess::Debug::Print(2,"PID of open pipe: $pid");
1017
print MYSQL_Q "select 'ok';\n";
1018
$answer = <MYSQL_A>; #answer from mysql
1019
MySQLaccess::Debug::Print(2,"Answer: $answer\n");
1020
foreach $nerror (sort(keys(%ACCESS_ERR))) {
1021
MySQLaccess::Debug::Print(3,"check answer for error $ACCESS_ERR{$nerror}");
1022
if (grep(/$ACCESS_ERR{$nerror}/i,$answer)) {
1023
MySQLaccess::Debug::Print(2,"Answer contain error [$nerror]");
1029
# check server-version
1030
print MYSQL_Q "select 'ok';\n";
1031
$answer = <MYSQL_A>; #answer from mysql
1032
MySQLaccess::Debug::Print(2,"Answer: $answer\n");
1033
foreach $nerror (sort(keys(%ACCESS_ERR))) {
1034
MySQLaccess::Debug::Print(3,"check answer for error $ACCESS_ERR{$nerror}");
1035
if (grep(/$ACCESS_ERR{$nerror}/i,$answer)) {
1036
MySQLaccess::Debug::Print(2,"Answer contain error [$nerror]");
1046
# ======================================
1047
# sub CloseConnection
1048
# Close the connection to the mysql-db
1049
# ======================================
1050
sub CloseConnection {
1055
# ===========================================================
1056
# sub CreateTable($table)
1057
# Create temporary/backup table
1058
# ===========================================================
1061
my ($table,$force) = @_;
1062
my %tables = ( $MySQLaccess::ACCESS_U_TMP => $MySQLaccess::ACCESS_U,
1063
$MySQLaccess::ACCESS_H_TMP => $MySQLaccess::ACCESS_H,
1064
$MySQLaccess::ACCESS_D_TMP => $MySQLaccess::ACCESS_D,
1065
$MySQLaccess::ACCESS_U_BCK => $MySQLaccess::ACCESS_U,
1066
$MySQLaccess::ACCESS_H_BCK => $MySQLaccess::ACCESS_H,
1067
$MySQLaccess::ACCESS_D_BCK => $MySQLaccess::ACCESS_D,
1068
$MySQLaccess::ACCESS_U => $MySQLaccess::ACCESS_U_BCK,
1069
$MySQLaccess::ACCESS_H => $MySQLaccess::ACCESS_H_BCK,
1070
$MySQLaccess::ACCESS_D => $MySQLaccess::ACCESS_D_BCK,
1077
my @known_tables=();
1079
# print STDERR "CreateTable($table)\n";
1080
MySQLaccess::Debug::Print(1,"CreateTable($table):");
1083
return 'Unknown_table' unless defined($tables{$table});
1085
## build list of known/existing tables;
1086
## if 'force' existing table is dropped first
1087
if (defined($force) and $force) {
1088
@known_tables = Show_Tables();
1089
if (grep(/^$table$/,@known_tables)) {
1090
$query = "DROP TABLE $table;";
1094
## path to mysqldump executable
1095
my $connect = $MySQLaccess::MYSQLDUMP;
1096
$connect .= " --defaults-file=$MySQLaccess::MYSQL_CNF --no-data";
1097
# superuser, spassword transmitted via defaults-file
1098
if (defined($MySQLaccess::Param{'rhost'})) { $connect .= " --host=$MySQLaccess::Param{'rhost'}"; }
1099
$connect .= " $MySQLaccess::ACCESS_DB";
1100
$connect .= " $tables{$table}";
1103
## get creation-data for original table
1105
my $mysqldump = $connect;
1106
$mysqldump =~ s/ \$TABLE / $tbl /;
1108
# open connection (not using /bin/sh -c)
1109
MySQLaccess::Debug::Print(2,"Connecting to: $connect");
1110
$pid=IPC::Open3::open3(\*DONTCARE,\*CREATE,"",split /\s+/,$mysqldump);
1111
MySQLaccess::Debug::Print(2,"PID of open pipe: $pid");
1112
#open(CREATE,"$mysqldump");
1114
$create = "@create";
1115
foreach $nerror (sort(keys(%ACCESS_ERR))) {
1116
MySQLaccess::Debug::Print(3,"check answer for error $ACCESS_ERR{$nerror}");
1117
if (grep(/$ACCESS_ERR{$nerror}/i,$create)) {
1118
MySQLaccess::Debug::Print(2,"Answer contain error [$nerror]");
1125
## manipulate result for creation-data for temporary table
1126
$create =~ s/CREATE TABLE $tables{$table} \(/CREATE TABLE $table \(/;
1128
## recreate temporary table
1129
$query .= "$create\n";
1130
$query .= "select 'ok';";
1133
print MYSQL_Q "$query\n";
1134
# print STDERR $query;
1136
$answer = <MYSQL_A>; #answer from mysql
1137
# print STDERR "A>",$answer;
1138
MySQLaccess::Debug::Print(2,"Answer: $answer\n");
1139
foreach $nerror (sort(keys(%ACCESS_ERR))) {
1140
# print STDERR "->$nerror?";
1141
MySQLaccess::Debug::Print(3,"check answer for error $ACCESS_ERR{$nerror}");
1142
if (grep(/$ACCESS_ERR{$nerror}/i,$answer)) {
1143
# print STDERR "Yes!";
1144
MySQLaccess::Debug::Print(2,"Answer contain error [$nerror]");
1149
$delim = <MYSQL_A>; # read header
1150
if ($delim ne "ok\n") {
1151
while (($line=<MYSQL_A>) ne "ok\n")
1152
{ MySQLaccess::Debug::Print(3," A> $line"); }
1153
$skip = <MYSQL_A>; # skip result 'ok'
1155
# print STDERR "CreateTable done\n";
1160
# ===========================================================
1162
# Copy the structure and the data of a table to another table
1163
# ===========================================================
1165
my ($from,$to,$force) = @_;
1166
my @known_tables = Show_Tables();
1171
# print STDERR "CopyTable($from,$to)\n";
1172
MySQLaccess::Debug::Print(1,"MySQLaccess::DB::CopyTable($from,$to)");
1175
if (!grep(/^$from$/,@known_tables)) { return 'Unknown_table'; }
1179
if (defined($force) and $force) {
1180
return $nerror if ($nerror=CreateTable($to,$force));
1181
# print STDERR "Structure copied\n";
1185
$query .= "DELETE FROM $to;";
1186
$query .= "INSERT INTO $to SELECT * FROM $from;";
1187
$query .= "SELECT 'ok';\n";
1188
MySQLaccess::Debug::Print(2,"Query: $query");
1191
print MYSQL_Q "$query\n";
1192
# print STDERR $query;
1194
## check for errors...
1195
my $answer = <MYSQL_A>; #answer from mysql
1196
# print STDERR $answer;
1197
MySQLaccess::Debug::Print(2,"Answer: $answer\n");
1198
foreach $nerror (sort(keys(%ACCESS_ERR))) {
1199
MySQLaccess::Debug::Print(3,"check answer for error $ACCESS_ERR{$nerror}");
1200
if (grep(/$ACCESS_ERR{$nerror}/i,$answer)) {
1201
MySQLaccess::Debug::Print(2,"Answer contain error [$nerror]");
1206
my $delim = <MYSQL_A>; # read header
1207
# print STDERR $delim;
1208
if ($delim ne "ok\n") {
1209
while (($line=<MYSQL_A>) ne "ok\n")
1210
{ MySQLaccess::Debug::Print(3," A> $line"); }
1211
$skip = <MYSQL_A>; # skip result 'ok'
1217
# ===========================================================
1218
# sub LoadTmpTables()
1219
# (Re)load temporary tables with entries of ACL-tables
1220
# ===========================================================
1222
my %tables = ( $MySQLaccess::ACCESS_U => $MySQLaccess::ACCESS_U_TMP,
1223
$MySQLaccess::ACCESS_H => $MySQLaccess::ACCESS_H_TMP,
1224
$MySQLaccess::ACCESS_D => $MySQLaccess::ACCESS_D_TMP,
1229
# print STDERR "LoadTmpTables:\n";
1230
MySQLaccess::Debug::Print(1,"LoadTmpTables():");
1231
foreach $tbl (keys(%tables)) {
1232
# print STDERR "$tbl -> $tables{$tbl}\n";
1233
MySQLaccess::Debug::Print(2,"Loading table $tbl -> $tables{$tbl}.");
1234
return $nerror if ($nerror=CopyTable($tbl,$tables{$tbl},'force'));
1239
# ===========================================================
1240
# sub BackupGrantTables()
1241
# Make a backup of the original grant-tables
1242
# ===========================================================
1243
sub BackupGrantTables {
1244
my %tables = ( $MySQLaccess::ACCESS_U => $MySQLaccess::ACCESS_U_BCK,
1245
$MySQLaccess::ACCESS_H => $MySQLaccess::ACCESS_H_BCK,
1246
$MySQLaccess::ACCESS_D => $MySQLaccess::ACCESS_D_BCK,
1251
# print STDERR "BackupGrantTables:\n";
1252
MySQLaccess::Debug::Print(1,"BackupGrantTables():");
1253
foreach $tbl (keys(%tables)) {
1254
# print STDERR "$tbl -> $tables{$tbl}\n";
1255
MySQLaccess::Debug::Print(2,"Backup table $tbl -> $tables{$tbl}.");
1256
return $nerror if ($nerror=CopyTable($tbl,$tables{$tbl},'force'));
1261
# ===========================================================
1262
# sub RollbackGrantTables()
1263
# Rollback the backup of the grant-tables
1264
# ===========================================================
1265
sub RollbackGrantTables {
1266
my %tables = ( $MySQLaccess::ACCESS_U_BCK => $MySQLaccess::ACCESS_U,
1267
$MySQLaccess::ACCESS_H_BCK => $MySQLaccess::ACCESS_H,
1268
$MySQLaccess::ACCESS_D_BCK => $MySQLaccess::ACCESS_D,
1273
# print STDERR "RollbackGrantTables:\n";
1274
MySQLaccess::Debug::Print(1,"RollbackGrantTables():");
1275
foreach $tbl (keys(%tables)) {
1276
# print STDERR "$tbl -> $tables{$tbl}\n";
1277
MySQLaccess::Debug::Print(2,"Rollback table $tbl -> $tables{$tbl}.");
1278
return $nerror if ($nerror=CopyTable($tbl,$tables{$tbl},'force'));
1284
# ===========================================================
1285
# sub CommitGrantTables()
1286
# Copy grant-rules from temporary tables to the ACL-tables
1287
# ===========================================================
1288
sub CommitGrantTables {
1289
my %tables = ( $MySQLaccess::ACCESS_U => $MySQLaccess::ACCESS_U_TMP,
1290
$MySQLaccess::ACCESS_H => $MySQLaccess::ACCESS_H_TMP,
1291
$MySQLaccess::ACCESS_D => $MySQLaccess::ACCESS_D_TMP,
1299
print STDERR "CommitGrantTables()\n";
1300
MySQLaccess::Debug::Print(1,"CommitGrantTables():");
1302
## Make backup of original grant-tables
1303
MySQLaccess::Debug::Print(2,"Making backup of original grant-tables...");
1304
BackupGrantTables();
1306
## Copy data from temporay tables to grant-tables
1307
foreach $tbl (keys(%tables)) {
1308
print STDERR "$tbl -> $tables{$tbl}\n";
1309
MySQLaccess::Debug::Print(2,"Loading data $tables{$tbl} -> $tbl.");
1310
return $nerror if ($nerror=CopyTable($tables{$tbl},$tbl));
1316
# ===========================================================
1317
# sub Show_Fields($table):
1318
# return (a reference to) a hash which holds the names
1319
# of all relevant grant-fields, with their index in the record,
1320
# and (a reference to) an array which holds the fieldnames.
1321
# ===========================================================
1324
my %skip = ('host' => [0,1]
1330
my $query = "show fields from $table;select 'ok';\n";
1334
#print STDERR $query;
1335
MySQLaccess::Debug::Print(1,"Show_Fields($table):");
1336
MySQLaccess::Debug::Print(2,"SQL: $query");
1338
print MYSQL_Q "$query";
1339
my $skip = <MYSQL_A>; #skip header
1340
while (($line=<MYSQL_A>) ne "ok\n")
1342
#print STDERR ">",$line;
1344
MySQLaccess::Debug::Print(2," $table>: $line");
1345
my ($field,$type,$null,$key,$default,$extra) = split(' ',$line);
1346
$field = ucfirst($field);
1347
MySQLaccess::Debug::Print(3, " <split: $field - $type - $null - $key - $default - $extra");
1348
if (! grep(/$i/,@{$skip{$table}}) ){
1349
$Struct{$field} = $i; #hash
1350
push(@Struct,$field); #array
1351
MySQLaccess::Debug::Print(3," ==> added column[$i]: $field ($Struct{$field})");
1354
MySQLaccess::Debug::Print(3," ==> skipped column[$i], value=[$field]");
1359
$skip=<MYSQL_A>; # Get ok row (found already ok header)
1361
MySQLaccess::Debug::Print(2, "Array:");
1362
foreach $field (@Struct) { MySQLaccess::Debug::Print(2,"+ $field"); }
1363
MySQLaccess::Debug::Print(2,"Hash:");
1364
foreach $field (keys(%Struct)) { MySQLaccess::Debug::Print(2,"+ $field -> $Struct{$field}"); }
1366
return (\%Struct,\@Struct);
1369
# ===========================================================
1370
# sub Show_Tables():
1371
# return (a reference to) an array which holds all
1373
# ===========================================================
1376
my $query = "show tables;select 'ok';\n";
1380
MySQLaccess::Debug::Print(1,"Show_Tables():");
1381
MySQLaccess::Debug::Print(2,"SQL: $query");
1383
print MYSQL_Q "$query";
1384
my $skip = <MYSQL_A>; #skip header
1385
while (($line=<MYSQL_A>) ne "ok\n")
1388
push(@Tables,$line); #array
1389
MySQLaccess::Debug::Print(3," ==> added table: $line");
1392
$skip=<MYSQL_A>; # Get ok row (found already ok header)
1394
MySQLaccess::Debug::Print(2, "Array:");
1395
foreach $tbl (@Tables) { MySQLaccess::Debug::Print(2,"+ $tbl"); }
1400
# ======================================
1401
# sub Validate_Password($passwd,$host,$user,$encpw)
1402
# Validate the given password
1404
# connecting from host '$host'
1405
# ======================================
1406
sub Validate_Password {
1407
my ($password,$host,$user,$encpw) = @_;
1410
MySQLaccess::Debug::Print(1,"Validate_Password($password,$host,$user,$encpw)");
1411
my $sql = "select host,user,password from user having "
1412
."host='$host' and user='$user' and password='$encpw' "
1413
."and password=PASSWORD('$password');\n";
1414
$sql .= "select 'ok';\n";
1415
MySQLaccess::Debug::Print(2,"SQL = $sql");
1416
print MYSQL_Q "$sql";
1418
# if password is valid, at least 1 row returns before we read 'ok'
1419
while ( ($line=<MYSQL_A>) ne "ok\n") {
1420
MySQLaccess::Debug::Print(2," A> $line");
1421
$valid = defined($line);
1423
my $skip = <MYSQL_A>; # read 'ok'
1429
# ==========================================================
1430
# sub Sort_fields: (rewritten by psmith)
1431
# Build the query for an ordered list of entries
1432
# ==========================================================
1434
my ($start, $end, $sofar, $this, @rest) = (@_);
1435
my @where = ("((FIELD not like '\\%') AND (FIELD <> ''))",
1436
"((FIELD like '%\\%%') OR (FIELD like '%\\_%'))",
1440
$this or return ("$start $sofar $end");
1442
$sofar .= ' AND ' if $sofar;
1444
foreach $w (@where) {
1446
$f =~ s/FIELD/$this/g;
1448
$res .= Sort_fields($start, $end, "$sofar$f", @rest);
1454
# ===========================================================
1455
# sub Sort_table: (rewritten by psmith)
1456
# return all entries in the given table,
1457
# in an ordered fashion
1458
# ===========================================================
1460
my ($tbl, @order) = @_;
1463
# as long as there's no full where clause (Distrib 3.20)...
1465
# NOTE: this clause WILL NOT work on 3.21, because of the
1466
# order of 'ORDER BY' and 'HAVING'
1467
my $start = "SELECT *,UCASE(host) as ucase_host FROM $tbl ";
1468
$start .= 'ORDER BY ' . join(',', @order) ." HAVING ";
1471
# server version 3.21 has a full where clause :-)
1472
if ($MySQLaccess::Host::SERVER >= '3.21') {
1473
# print "+++USING FULL WHERE CLAUSE+++\n";
1474
$start = "SELECT *,UCASE(host) as ucase_host FROM $tbl WHERE ";
1475
$end = ' ORDER BY ' . join(',', @order) . ";\n";
1478
MySQLaccess::Debug::Print(1,"Sort_table():");
1479
MySQLaccess::Debug::Print(2,"Sorting table $tbl by `@order'");
1482
foreach $tmp (@order)
1484
$tmp="UCASE(host)" if ($tmp eq "ucase_host");
1486
my $query = Sort_fields($start, $end, '', @order);
1487
$query .= "select 'ok';\n";
1488
MySQLaccess::Debug::Print(2,"Query: $query");
1490
print MYSQL_Q "$query\n";
1492
my $delim = <MYSQL_A>; # read header
1493
MySQLaccess::Debug::Print(3," A> $delim");
1494
if ($delim ne "ok\n") {
1495
if ($delim =~ /^ERROR/) {
1496
push(@MySQLaccess::Grant::Error,'use_old_server');
1497
MySQLaccess::Report::Print_Error_Messages() ;
1500
while (($line=<MYSQL_A>) ne "ok\n")
1502
MySQLaccess::Debug::Print(3," A> $line");
1506
my $skip = <MYSQL_A>; # skip result 'ok'
1508
# remove columnheaders from output
1509
@res = grep(!/^\Q$delim\E$/, @res);
1510
# remove trailing \n from each returned record
1512
# each record has 1 field to much : ucase_host
1513
@res = grep { /(.*)\t.*$/; $_ = $1; } @res;
1515
MySQLaccess::Debug::Print(2,"Result of sorted table $tbl:");
1516
foreach $line (@res) { MySQLaccess::Debug::Print(2," >>$line"); }
1520
# ===========================================================
1521
# sub Get_All_db(template):
1522
# return all db the grant-tables are working on,
1523
# which conform to the template
1524
# ===========================================================
1526
my ($template,$tmp) = @_;
1530
# working with temporary tables or production tables
1531
if (defined($tmp) and $tmp) {
1532
$aref = \@MySQLaccess::Grant::sorted_db_tmp_table ;
1535
$aref = \@MySQLaccess::Grant::sorted_db_table;
1538
MySQLaccess::Debug::Print(1," template=[$template]");
1540
# get all db for which access-rights can be calculated,
1541
# which conform to the template.
1542
# !! these db's don't have to exist yet, so it's not
1543
# enough to look which db already exist on the system
1544
$reg_expr = $template;
1545
if ($template =~ /[\*\?]/) {
1546
$reg_expr =~ tr/*?/%_/;
1547
#$reg_expr = MySQLaccess::Wildcards::Wild2Reg($template);
1549
$reg_expr = MySQLaccess::Wildcards::SQL2Reg("$reg_expr");
1551
if ( ! ($template =~ /[\*\?%_]/) ) {
1552
push(@db,$template);
1556
MySQLaccess::Debug::Print(2,"#Reading db-table...");
1557
foreach $record (@{$aref}) { #MySQLaccess::Grant::sorted_db_table) {
1558
my @record=split(/\t/,$record);
1559
my $db = $record[1];
1560
MySQLaccess::Debug::Print(2,"> $db ");
1561
if ( (!grep(/$db/i,@db)) and ($db =~/$reg_expr/i) ) {
1563
MySQLaccess::Debug::Print(2,"added");
1566
MySQLaccess::Debug::Print(2,"skipped");
1569
# if no rule is found for a certain db in the db-table,
1570
# the rights of the user are used, so we should inform
1572
if (!grep(/^%$/,@db)) { push(@db,"$MySQLaccess::NEW_DB"); }
1576
# ===========================================================
1577
# sub Get_All_users(template):
1578
# return all users the grant-tables are working on,
1579
# which conform to the template
1580
# ===========================================================
1582
($template,$tmp) = @_; # nog verder uitwerken!!!
1586
# working with temporary tables or production tables
1587
if (defined($tmp) and $tmp) {
1588
$aref = \@MySQLaccess::Grant::sorted_user_tmp_table ;
1591
$aref = \@MySQLaccess::Grant::sorted_user_table;
1594
MySQLaccess::Debug::Print(1,"Debug Get_All_users:");
1595
# get all db for which access-rights can be calculated.
1596
# !! these db's don't have to exist yet, so it's not
1597
# enough to look which db already exist on the system
1598
$reg_expr = $template;
1599
if ($template =~ /[\*\?]/) {
1600
$reg_expr =~ tr/*?/%_/;
1601
#$reg_expr = MySQLaccess::Wildcards::Wild2Reg($template);
1603
$reg_expr = MySQLaccess::Wildcards::SQL2Reg("$reg_expr");
1605
if ( ! ($template =~ /[\*\?%_]/) ) {
1606
push(@user,$template);
1610
MySQLaccess::Debug::Print(2,"#Reading user-table...");
1611
foreach $record (@{$aref}) { #MySQLaccess::Grant::sorted_user_table) {
1612
my @record=split(/\t/,$record);
1613
my $user = $record[1];
1614
MySQLaccess::Debug::Print(2,"> $user ");
1615
if ( (!grep(/$user/,@user)) and ($user=~/$reg_expr/)) {
1617
MySQLaccess::Debug::Print(2, "added");
1620
MySQLaccess::Debug::Print(2, "skipped");
1623
# Any user means also:
1624
# - the 'empty' user, ie without supplying a username
1625
# - any user still to be defined/created
1626
#push(@user,''); #without_suplying_a_username
1627
push(@user,"$MySQLaccess::NEW_USER");
1628
#push(@Warnings,'minimum_priv');
1632
# ===========================================================
1633
# sub Get_All_hosts(template):
1634
# return all hosts the grant-tables are working on,
1635
# which conform to the template
1636
# ===========================================================
1638
my ($template,$tmp) = @_;
1643
# working with temporary tables or production tables
1644
if (defined($tmp) and $tmp) {
1645
$aref = \@MySQLaccess::Grant::sorted_host_tmp_table ;
1646
$aref1= \@MySQLaccess::Grant::sorted_db_tmp_table ;
1649
$aref = \@MySQLaccess::Grant::sorted_host_table;
1650
$aref1= \@MySQLaccess::Grant::sorted_db_table ;
1653
MySQLaccess::Debug::Print(1, "Debug Get_All_hosts:");
1654
# get all db for which access-rights can be calculated.
1655
# !! these db's don't have to exist yet, so it's not
1656
# enough to look which db already exist on the system
1657
$reg_expr = $template;
1658
if ($template =~ /[\*\?]/) {
1659
$reg_expr =~ tr/*?/%_/;
1660
#$reg_expr = MySQLaccess::Wildcards::Wild2Reg($template);
1662
$reg_expr = MySQLaccess::Wildcards::SQL2Reg("$reg_expr");
1664
if ( ! ($template =~ /[\*\?%_]/) ) {
1665
push(@host,$template);
1669
MySQLaccess::Debug::Print(1, "#Reading db-table...");
1670
foreach $record (@{$aref1}) { #MySQLaccess::Grant::sorted_db_table) {
1671
my @record=split(/\t/,$record);
1672
my $host = $record[0];
1673
MySQLaccess::Debug::Print(2, "> $host ");
1674
if (! grep(/$host/i,@host)) {
1676
MySQLaccess::Debug::Print(2, "added");
1679
MySQLaccess::Debug::Print(2, "skipped");
1682
MySQLaccess::Debug::Print(1, "#Reading host-table...");
1683
foreach $record (@{$aref}) {
1684
my @record=split(/\t/,$record);
1685
my $host = $record[0];
1686
MySQLaccess::Debug::Print(2, "> $host ");
1687
if ( (!grep(/$host/,@host)) and ($host=~/$reg_expr/)) {
1689
MySQLaccess::Debug::Print(2, "added");
1692
MySQLaccess::Debug::Print(2, "skipped");
1696
#print "#Reading user-table...\n" if ($DEBUG>1);
1697
#foreach $record (@MySQLaccess::Grant::sorted_user_table) {
1698
# my @record=split(/\t/,$record);
1699
# my $host = $record[0];
1700
# print "> $host " if ($DEBUG>2);
1701
# if ( (!grep(/$host/,@host)) and ($host=~/$reg_expr/)) {
1702
# push(@host,$host);
1703
# print "added\n" if ($DEBUG>2);
1706
# print "skipped\n" if ($DEBUG>2);
1709
# Any host also means:
1710
# - any host still to be defined/created
1711
#push(@host,"any_other_host");
1713
@host = sort(@host);
1718
##########################################################################
1719
package MySQLaccess::Grant;
1723
$DEBUG = $MySQLaccess::DEBUG unless ($DEBUG);
1728
# ===========================================================
1729
# sub Diff_Privileges()
1730
# Calculate diff between temporary and original grant-tables
1731
# ===========================================================
1732
sub Diff_Privileges {
1737
# -----------------------------
1738
# Build list of users,dbs,hosts
1740
my @all_dbs = @{MySQLaccess::DB::Get_All_dbs('*')};
1741
my @all_users = @{MySQLaccess::DB::Get_All_users('*')};
1742
my @all_hosts = @{MySQLaccess::DB::Get_All_hosts('*')};
1744
my @all_dbs_tmp = @{MySQLaccess::DB::Get_All_dbs('*','tmp')};
1745
my @all_users_tmp = @{MySQLaccess::DB::Get_All_users('*','tmp')};
1746
my @all_hosts_tmp = @{MySQLaccess::DB::Get_All_hosts('*','tmp')};
1750
# ------------------------------------
1751
# Build list of priv. for grant-tables
1752
foreach $host (@all_hosts) {
1753
foreach $user (@all_users) {
1754
foreach $db (@all_dbs) {
1755
MySQLaccess::Grant::Initialize();
1756
%Access = MySQLaccess::Grant::Get_Access_Rights($host,$user,$db);
1757
push(@before,MySQLaccess::Report::Raw_Report($host,$user,$db,\%Access));
1762
# ----------------------------------
1763
# Build list of priv. for tmp-tables
1764
foreach $host (@all_hosts_tmp) {
1765
foreach $user (@all_users_tmp) {
1766
foreach $db (@all_dbs_tmp) {
1767
MySQLaccess::Grant::Initialize('tmp');
1768
%Access = MySQLaccess::Grant::Get_Access_Rights($host,$user,$db,'tmp');
1769
push(@after,MySQLaccess::Report::Raw_Report($host,$user,$db,\%Access));
1774
# ----------------------------------
1775
# Write results to temp-file to make
1777
@before = sort(@before);
1778
@after = sort(@after);
1780
($hb, $before) = tempfile("$MySQLaccess::script.XXXXXX") or
1781
push(@MySQLaccess::Report::Errors,"Can't create temporary file: $!");
1782
($ha, $after) = tempfile("$MySQLaccess::script.XXXXXX") or
1783
push(@MySQLaccess::Report::Errors,"Can't create temporary file: $!");
1785
print $hb join("\n",@before);
1786
print $ha join("\n",@after);
1790
# ----------------------------------
1791
# compute difference
1792
my $cmd="$MySQLaccess::DIFF $before $after |";
1795
@diffs = grep(/[<>]/,@diffs);
1799
# ----------------------------------
1800
# cleanup temp. files
1807
# ===========================================================
1810
# ===========================================================
1812
%MySQLaccess::Grant::Access = %{Default_Access_Rights()};
1813
@MySQLaccess::Grant::Errors = ();
1814
@MySQLaccess::Grant::Warnings = ();
1815
@MySQLaccess::Grant::Notes = ();
1818
$MySQLaccess::Grant::Rules{'user'} = 'no_rule_found';
1819
$MySQLaccess::Grant::Rules{'db'} = 'no_rule_found';
1820
$MySQLaccess::Grant::Rules{'host'} = 'no_equiv_host';
1821
$MySQLaccess::Grant::full_access = 1;
1823
$MySQLaccess::Grant::process_host_table = 0;
1827
# ===========================================================
1830
# ===========================================================
1833
my ($HOST,$DB,$USER);
1836
# build list of available tables
1837
@tables = MySQLaccess::DB::Show_Tables();
1839
# reading production grant-tables or temporary tables?
1840
$tmp = (defined($tmp) and $tmp) ? 1 : 0;
1841
if ($tmp) { #reading temporary tables
1842
$HOST=$MySQLaccess::ACCESS_H_TMP;
1843
$DB =$MySQLaccess::ACCESS_D_TMP;
1844
$USER=$MySQLaccess::ACCESS_U_TMP;
1846
# ----------------------------
1848
if (!grep(/$HOST/,@tables)) { MySQLaccess::DB::CreateTable($HOST); }
1849
if (!grep(/$USER/,@tables)) { MySQLaccess::DB::CreateTable($USER); }
1850
if (!grep(/$DB/,@tables)) { MySQLaccess::DB::CreateTable($DB); }
1852
MySQLaccess::Debug::Print(1,"Finding fields in tmp-ACL files:");
1853
# -----------------------------
1855
my ($h1,$h2) = MySQLaccess::DB::Show_Fields($HOST);
1856
my ($d1,$d2) = MySQLaccess::DB::Show_Fields($DB);
1857
my ($u1,$u2) = MySQLaccess::DB::Show_Fields($USER);
1858
%MySQLaccess::Grant::H_tmp = %{$h1}; @MySQLaccess::Grant::H_tmp = @{$h2};
1859
%MySQLaccess::Grant::D_tmp = %{$d1}; @MySQLaccess::Grant::D_tmp = @{$d2};
1860
%MySQLaccess::Grant::U_tmp = %{$u1}; @MySQLaccess::Grant::U_tmp = @{$u2};
1862
# @MySQLaccess::Grant::Privileges_tmp=@{Make_Privlist()};
1864
MySQLaccess::Debug::Print(1, "Reading sorted temp-tables:");
1865
@MySQLaccess::Grant::sorted_db_tmp_table = MySQLaccess::DB::Sort_table($DB, 'ucase_host', 'user', 'db');
1866
@MySQLaccess::Grant::sorted_host_tmp_table= MySQLaccess::DB::Sort_table($HOST, 'ucase_host', 'db');
1867
@MySQLaccess::Grant::sorted_user_tmp_table= defined($MySQLaccess::Param{'password'}) ?
1868
MySQLaccess::DB::Sort_table($USER, 'ucase_host', 'user', 'password'):
1869
MySQLaccess::DB::Sort_table($USER, 'ucase_host', 'user');
1871
else { #reading production grant-tables
1872
$HOST=$MySQLaccess::ACCESS_H;
1873
$DB =$MySQLaccess::ACCESS_D;
1874
$USER=$MySQLaccess::ACCESS_U;
1876
MySQLaccess::Debug::Print(1,"Finding fields in ACL files:");
1877
# -----------------------------
1879
my ($h1,$h2) = MySQLaccess::DB::Show_Fields($HOST);
1880
my ($d1,$d2) = MySQLaccess::DB::Show_Fields($DB);
1881
my ($u1,$u2) = MySQLaccess::DB::Show_Fields($USER);
1882
%MySQLaccess::Grant::H = %{$h1}; @MySQLaccess::Grant::H = @{$h2};
1883
%MySQLaccess::Grant::D = %{$d1}; @MySQLaccess::Grant::D = @{$d2};
1884
%MySQLaccess::Grant::U = %{$u1}; @MySQLaccess::Grant::U = @{$u2};
1886
@MySQLaccess::Grant::Privileges=@{Make_Privlist()};
1888
MySQLaccess::Debug::Print(1, "Reading sorted tables:");
1889
@MySQLaccess::Grant::sorted_db_table = MySQLaccess::DB::Sort_table($DB, 'ucase_host', 'user', 'db');
1890
@MySQLaccess::Grant::sorted_host_table= MySQLaccess::DB::Sort_table($HOST, 'ucase_host', 'db');
1891
@MySQLaccess::Grant::sorted_user_table= defined($MySQLaccess::Param{'password'}) ?
1892
MySQLaccess::DB::Sort_table($USER, 'ucase_host', 'user', 'password'):
1893
MySQLaccess::DB::Sort_table($USER, 'ucase_host', 'user');
1899
# ===========================================================
1900
# sub Get_Access_Rights(host,user,db)
1901
# report the access_rights for the tuple ($host,$user,$db).
1902
# ===========================================================
1903
sub Get_Access_Rights {
1904
local ($host,$user,$db,$tmp) = @_;
1909
# working with temporary tables or production tables
1910
if (defined($tmp) and $tmp) {
1911
$aref_user = \@MySQLaccess::Grant::sorted_user_tmp_table;
1912
$aref_host = \@MySQLaccess::Grant::sorted_host_tmp_table;
1913
$aref_db = \@MySQLaccess::Grant::sorted_db_tmp_table;
1916
$aref_user = \@MySQLaccess::Grant::sorted_user_table;
1917
$aref_host = \@MySQLaccess::Grant::sorted_host_table;
1918
$aref_db = \@MySQLaccess::Grant::sorted_db_table;
1922
my ($refrecord,$refgrant);
1923
my ($_host_,$_user_,$encpw_);
1926
MySQLaccess::Debug::Print(1, "for ($host,$user,$db):");
1928
# ******************************************************************************
1929
# Create default access-rights
1930
# default access-rights are no access at all!!
1933
# ******************************************************************************
1934
# get hostname for IP-address
1935
# get IP-address for hostname
1936
local $host_name = MySQLaccess::Host::IP2Name($host);
1937
local $host_ip = MySQLaccess::Host::Name2IP($host);
1939
MySQLaccess::Debug::Print(3,"host=$host, hostname=$host_name, host-ip =$host_ip");
1940
MySQLaccess::Debug::Print(3,"user=$user");
1941
MySQLaccess::Debug::Print(3,"db =$db");
1943
# ***********************************************************************
1944
# retrieve information on USER
1945
# check all records in mysql::user for matches with the tuple (host,user)
1946
# ***********************************************************************
1947
# 4.OR (add) the privileges for the user from the "user" table.
1948
# (add all privileges which is "Y" in "user")
1949
($refrecord,$refgrant) = Get_grant_from_user($host,$user,$aref_user);
1950
($_host_,$_user_,$encpw_) = @{$refrecord};
1951
%_access_ = %{$refgrant};
1953
foreach $field (keys(%U)) { ##only priv. set in user-table
1954
$MySQLaccess::Grant::Access{$field} = ($MySQLaccess::Grant::Access{$field} or $_access_{$field});
1957
if ($_user_ eq $MySQLaccess::NEW_USER) {
1958
push(@Warnings,'minimum_priv');
1960
if ($_user_ ne $user) {
1962
push(@Warnings,'anonymous_access');
1965
# *******************************************************
1966
# Validate password if this has been asked to do
1967
# *******************************************************
1968
if (defined($password)) {
1969
$valid = Validate_Password($password,$_host_,$_user_,$_encpw_,$aref_user);
1970
if (!$valid) { push(@Errors,'invalid_password'); }
1971
else { push(@Notes,'valid_password'); }
1974
# ******************************************************************************
1975
# retrieve information on DB
1976
# check all records in mysql::db for matches with the triple (host,db,user)
1977
# first match is used.
1978
# ******************************************************************************
1979
# 2.Get grant for user from the "db" table.
1981
($refrecord,$refgrant)=Get_grant_from_db($host,$db,$user,$aref_db); #set process_host_table
1982
($_host_,$_user_,$encpw_) = @{$refrecord};
1983
%_access_ = %{$refgrant};
1985
foreach $field (keys(%D)) { ##only priv. set in db-table
1986
$MySQLaccess::Grant::Access{$field} = ($MySQLaccess::Grant::Access{$field} or $_access_{$field});
1989
# ***********************************************************************
1990
# retrieve information on HOST
1991
# check all records in mysql::host for matches with the tuple (host,db)
1993
# ' The host table is mainly to maintain a list of "secure" servers. '
1994
# ***********************************************************************
1995
# 3.If hostname is "empty" for the found entry, AND the privileges with
1996
# the privileges for the host in "host" table.
1997
# (Remove all which is not "Y" in both)
1999
if ($MySQLaccess::Grant::process_host_table) {
2000
($refrecord,$refgrant)=Get_grant_from_host($host,$db,$aref_host);
2001
($_host_,$_user_,$encpw_) = @{$refrecord};
2002
%_access_ = %{$refgrant};
2004
foreach $field (keys(%H)) { ##only priv. set in host-table
2005
$MySQLaccess::Grant::Access{$field} = ($MySQLaccess::Grant::Access{$field} and $_access_{$field});
2009
MySQLaccess::Debug::Print(1,"done for ($host,$user,$db)");
2010
return %MySQLaccess::Grant::Access;
2013
# ####################################
2014
# FINDING THE RIGHT GRANT-RULE
2015
# ==========================================================
2016
# sub Get_grant_from_user:
2017
# ==========================================================
2018
sub Get_grant_from_user {
2019
my ($host,$user,$aref) = @_;
2021
MySQLaccess::Debug::Print(1, "");
2022
MySQLaccess::Debug::Print(1, "(host=$host,user=$user)");
2024
my %Access_user = %{Default_Access_Rights()};
2030
foreach $record (@{$aref}) {
2031
$MySQLaccess::Grant::full_access=0;
2032
MySQLaccess::Debug::Print(3, "Record= $record");
2033
@record=split(/\t/,$record);
2036
# with possible wildcards in field
2037
# replace mysql-wildcards by reg-wildcards
2038
my $host_tpl = MySQLaccess::Wildcards::SQL2Reg($record[0]);
2039
my $user_tpl = $record[1]; #user field isn't pattern-matched!!
2040
my $passwd = $record[2];
2042
MySQLaccess::Debug::Print(3, "=>host_tpl : read=$record[0] -> converted=$host_tpl");
2043
MySQLaccess::Debug::Print(3, "=>user_tpl : read=$record[1] -> $user_tpl");
2044
MySQLaccess::Debug::Print(3, "=>password : read=$record[2] -> $passwd");
2047
if ( MySQLaccess::Host::MatchTemplate($host,$host_tpl) and
2048
MySQLaccess::Wildcards::MatchTemplate($user_tpl,$user)
2051
MySQLaccess::Debug::Print(2, "FOUND!!");
2052
if ($passwd eq '') { push(@Warnings,'insecure_user'); }
2053
else { push(@Notes,'password_required'); }
2055
foreach $field (keys(%U)) {
2056
$Access_user{$field} = $MySQLaccess::Report::Answer{$record[$U{$field}]};
2058
#print "\n" if $DEBUG;
2059
$MySQLaccess::Grant::Rules{'user'} = $record;
2065
# -------------------------------
2066
# setting privileges to user-priv
2067
MySQLaccess::Debug::Print(2, "Rights after parsing user-table..:");
2068
if (! $rule_found ) {
2070
MySQLaccess::Debug::Print(2, "NO record found in the user-table!!");
2073
MySQLaccess::Debug::Print(2, "Selected record=@record");
2074
MySQLaccess::Debug::Print(2, "<=?=> $record");
2077
MySQLaccess::Debug::Print(1, "returning @record");
2079
return (\@record,\%Access_user); #matching record in user-table
2082
# ==========================================================
2083
# sub Get_grant_from_db:
2084
# ==========================================================
2085
sub Get_grant_from_db {
2086
my ($host,$db,$user,$aref) = @_;
2088
MySQLaccess::Debug::Print(1, "(host=$host,user=$user,db=$db)");
2090
my %Access_db = %{Default_Access_Rights()};
2093
foreach $record (@{$aref}) {
2095
MySQLaccess::Debug::Print(2, "Read db: $record");
2096
@record=split(/\t/,$record);
2099
# with possible wildcards in field
2100
# replace mysql-wildcards by reg-wildcards
2101
my $host_tpl = MySQLaccess::Wildcards::SQL2Reg($record[0]);
2102
my $db_tpl = MySQLaccess::Wildcards::SQL2Reg($record[1]);
2103
my $user_tpl = $record[2]; #user field isn't pattern matched!!
2104
MySQLaccess::Debug::Print(3, "=>host_tpl : read=$record[0] -> converted=$host_tpl");
2105
MySQLaccess::Debug::Print(3, "=>db_tpl : read=$record[1] -> $db_tpl");
2106
MySQLaccess::Debug::Print(3, "=>user_tpl : read=$record[2] -> $user_tpl");
2108
if ( ( MySQLaccess::Host::Is_localhost($host_tpl)
2109
or MySQLaccess::Wildcards::MatchTemplate($host_tpl,$host_name)
2110
or MySQLaccess::Wildcards::MatchTemplate($host_tpl,$host_ip) )
2111
and ( MySQLaccess::Wildcards::MatchTemplate($db_tpl,$db) )
2112
and ( MySQLaccess::Wildcards::MatchTemplate($user_tpl,$user) ) ) {
2114
$MySQLaccess::Grant::process_host_table = ($record[0] eq '');
2116
if ($user_tpl eq '') { push(@Warnings,'public_database'); }
2118
foreach $field (keys(%D)) {
2119
$Access_db{$field} = $MySQLaccess::Report::Answer{$record[$D{$field}]};
2122
$MySQLaccess::Grant::Rules{'db'} = $record;
2127
# -------------------------------
2128
# setting privileges to db-priv
2129
MySQLaccess::Debug::Print(2, "Rights after parsing db-table..:");
2130
if (! $rule_found ) {
2131
MySQLaccess::Debug::Print(2, "NO rule found in db-table => no access granted!!");
2134
return (\@record,\%Access_db);
2137
# ==========================================================
2138
# sub Get_grant_from_host:
2139
# ==========================================================
2140
sub Get_grant_from_host {
2141
my ($host,$db,$aref) = @_;
2143
MySQLaccess::Debug::Print(1, "Get_grant_from_host()");
2145
my %Access_host = %{Default_Access_Rights()};
2147
# the host-table doesn't have to be processed if the host-field
2148
# in the db-table isn't empty
2149
if (!$MySQLaccess::Grant::process_host_table) {
2150
MySQLaccess::Debug::Print(2, ">> Host-table doesn't have to be processed!!");
2151
$MySQLaccess::Grant::Rules{'host'} = 'no_equiv_host';
2152
return ([],\%Access_host);
2158
foreach $record (@{$aref}) {
2160
MySQLaccess::Debug::Print(2, "host: $record");
2161
@record=split(/\t/,$record);
2164
# with possible wildcards in field
2165
# replace mysql-wildcards by reg-wildcards
2166
my $host_tpl = MySQLaccess::Wildcards::SQL2Reg($record[0]);
2167
my $db_tpl = MySQLaccess::Wildcards::SQL2Reg($record[1]);
2168
MySQLaccess::Debug::Print(3, "=>host_tpl : $record[0] -> $host_tpl");
2169
MySQLaccess::Debug::Print(3, "=>db_tpl : $record[1] -> $db_tpl");
2171
if ( ( MySQLaccess::Host::Is_localhost($host_tpl)
2172
or MySQLaccess::Wildcards::MatchTemplate($host_tpl,$host_name)
2173
or MySQLaccess::Wildcards::MatchTemplate($host_tpl,$host_ip) )
2174
and ( MySQLaccess::Wildcards::MatchTemplate($db_tpl,$db) ) ) {
2176
$MySQLaccess::Grant::Rules{'host'} = $record;
2178
foreach $field (keys(%H)) {
2179
$Access_host{$field} = $MySQLaccess::Report::Answer{$record[$H{$field}]};
2185
# -------------------------------
2186
# setting privileges to host-priv
2187
MySQLaccess::Debug::Print(2, "Rights after parsing host-table..:");
2188
if (! $rule_found ) {
2190
MySQLaccess::Debug::Print(2, "NO restrictions found in the host-table!!");
2193
# --------------------------------
2194
# debugging access-rights in db
2196
return (\@record,\%Access_host); #matching record in host-table
2201
# ===========================================================
2202
# sub Default_Access_Rights():
2203
# return (a reference to) a hash which holds all default
2204
# priviliges currently defined in the grant-tables.
2205
# ===========================================================
2206
sub Default_Access_Rights {
2209
MySQLaccess::Debug::Print(2, "Debug Default_Access_Rights():");
2210
# add entry for all fields in the HOST-table
2211
foreach $field (keys(%MySQLaccess::Grant::H)) {
2212
$right{$field}='0' unless (defined($right{$field}));
2214
# add entry for all fields in the DB-table
2215
foreach $field (keys(%MySQLaccess::Grant::D)) {
2216
$right{$field}='0' unless (defined($right{$field}));
2218
# add entry for all fields in the USER-table
2219
foreach $field (keys(%MySQLaccess::Grant::U)) {
2220
$right{$field}='0' unless (defined($right{$field}));
2224
foreach $field (keys(%right)) { MySQLaccess::Debug::Print(3, sprintf("> %15s : %1s",$field,$right{$field})); }
2229
# ======================================
2231
# Make an ordered list of the privileges
2232
# that should be reported
2233
# ======================================
2236
#'select_priv', 'create_priv',
2237
#'insert_priv', 'drop_priv',
2238
#'update_priv', 'reload_priv',
2239
#'delete_priv', 'process_priv',
2240
#'file_priv', 'shutdown_priv');
2243
foreach $right (@U) {
2244
if (! grep(/$right/,@privlist)) { push(@privlist,$right); }
2246
foreach $right (@D) {
2247
if (! grep(/$right/,@privlist)) { push(@privlist,$right); }
2249
foreach $right (@H) {
2250
if (! grep(/$right/,@privlist)) { push(@privlist,$right); }
2252
# print "Privileges:\n";
2253
# foreach $field (@privlist) { print " > $field\n"; }
2259
########################################################################
2260
package MySQLaccess::Report;
2262
@EXPORT = qw(&Print_Header());
2264
$FORM = $ENV{'SCRIPT_NAME'};
2266
$DEBUG = $MySQLaccess::DEBUG unless ($DEBUG);
2268
# translation-table for poss. answers
2269
%Answer = ('Y' => 1 , 'N' => 0
2270
, 1 => 'Y', 0 => 'N'
2271
,'?' => '?', '' => '?'
2276
# ****************************
2277
# Notes and warnings
2280
=> "Everybody can access your DB as user `\$user' from host `\$host'\n"
2281
."WITHOUT supplying a password.\n"
2282
."Be very careful about it!!"
2283
,'password_required'
2284
=> "A password is required for user `\$user' :-("
2286
=> "The password '\$password' for user `\$user' is invalid :-P"
2288
=> "You supplied the right password for user `\$user' :-)"
2290
=> "Any user with the appropriate permissions has access to your DB!\n"
2291
."Check your users!"
2293
=> "All grant-tables are empty, which gives full access to ALL users !!"
2295
=> "No matching rule"
2297
=> "Not processed: host-field is not empty in db-table."
2299
=> "If the final priveliges of the user are more then you gave the user,\n"
2300
."check the priveliges in the db-table `\$db'."
2302
=> "The privileges for any new user are AT LEAST\n"
2303
."the ones shown in the table above,\n"
2304
."since these are the privileges of the db `\$db'.\n"
2306
=> "The MySQL client program <$MySQLaccess::MYSQL> could not be found.\n"
2307
."+ Check your path, or\n"
2308
."+ edit the source of this script to point \$MYSQL to the mysql client.\n"
2309
,'not_found_mysqldump'
2310
=> "The MySQL dump program <$MySQLaccess::MYSQLDUMP> could not be found.\n"
2311
."+ Check your path, or\n"
2312
."+ edit the source of this script to point \$MYSQLDUMP to the mysqldump program.\n"
2314
=> "The diff program <$MySQLaccess::DIFF> could not be found.\n"
2315
."+ Check your path, or\n"
2316
."+ edit the source of this script to point \$DIFF to the diff program.\n"
2317
,'Unrecognized_option'
2319
."You are using an old version of the mysql-program,\n"
2320
."which does not yet implement a neccessary option.\n"
2322
."You need at least Version 6.2 of the mysql-client,\n"
2323
."which was build in MySQL v3.0.18, to use this version\n"
2324
."of `$MySQLaccess::script'."
2327
."An error occured when trying to connect to the database\n"
2328
."with the grant-tables:\n"
2329
."* Maybe YOU do not have READ-access to this database?\n"
2330
."* If you used the -U option, you may have supplied an invalid username?\n"
2331
." for the superuser?\n"
2332
."* If you used the -U option, it may be possible you have to supply\n"
2333
." a superuser-password to, with the -P option?\n"
2334
."* If you used the -P option, you may have supplied an invalid password?\n"
2337
."An error occured when trying to connect to the database\n"
2338
."with the grant-tables. (dbaccess denied)\n"
2339
,'Unknown_tmp_table'
2341
."An error occured when trying to work with the temporary tables in the database\n"
2342
."with the grant-tables. (One of the temporary tables does not exist)\n"
2345
."An error occured when trying to work with some tables in the database\n"
2346
."with the grant-tables. (table does not exist)\n"
2349
."An error occured when executing an SQL statement.\n"
2350
."You might consider altering the use of the parameter `--old_server' when \n"
2351
."calling `$MySQLaccess::script'."
2354
."An error occured when trying to connect to the database\n"
2355
."with the grant-tables. (unknown error)\n"
2357
=> "Accessing the db as an anonymous user.\n"
2358
."Your username has no relevance\n"
2360
=> "You have to supply a userid."
2362
=> "You have to supply the name of a database."
2364
=> "You have to supply the name of a host."
2369
# =====================================
2372
# =====================================
2374
if ($MySQLaccess::CMD) { #command-line mode
2375
print "$MySQLaccess::script Version $MySQLaccess::VERSION\n"
2376
."By RUG-AIV, by Yves Carlier (Yves.Carlier\@rug.ac.be)\n"
2377
."Changes by Steve Harvey (sgh\@vex.net)\n"
2378
."This software comes with ABSOLUTELY NO WARRANTY.\n";
2380
if ($MySQLaccess::CGI) { #CGI-BIN mode
2381
print "content-type: text/html\n\n"
2384
."<TITLE>MySQLaccess</TITLE>\n"
2387
."<H1>$MySQLaccess::script Version $MySQLaccess::VERSION</H1>\n"
2388
."<CENTER>\n<ADDRESS>\n"
2389
."By RUG-AIV, by Yves Carlier (<a href=mailto:Yves.Carlier\@rug.ac.be>Yves.Carlier\@rug.ac.be</a>)<BR>\n"
2390
."Changes by Steve Harvey (<a href=mailto:sgh\@vex.net>sgh\@vex.net</a>)<BR>\n"
2391
."This software comes with ABSOLUTELY NO WARRANTY.<BR>\n"
2392
."</ADDRESS>\n</CENTER>\n"
2400
# =====================================
2403
# =====================================
2405
if ($MySQLaccess::CMD) { #command-line mode
2407
."BUGs can be reported by email to bugs\@mysql.com\n";
2409
if ($MySQLaccess::CGI) { #CGI-BIN mode
2410
if ($MySQLaccess::Param{'brief'}) {
2411
print "</table>\n"; #close table in brief-output
2415
."BUGs can be reported by email to <a href=mailto:bugs\@mysql.com>bugs\@mysql.com</a><BR>\n"
2416
# ."Don't forget to mention the version $VERSION!<BR>\n"
2424
# =====================================
2425
# sub Print_Taskbar:
2426
# print taskbar on STDOUT
2427
# =====================================
2430
."[<a href=$FORM?relnotes=on>Release Notes</a>] \n"
2431
."[<a href=$FORM?version=on>Version</a>] \n"
2432
."[<a href=$FORM?plan=on>Future Plans</a>] \n"
2433
."[<a href=$FORM?howto=on>Examples</a>] \n"
2434
."[<a href=$FORM?help=on>New check</a>] \n"
2435
."[<a href=$FORM?edit=on>Change/edit ACL</a>] \n"
2440
# =====================================
2443
# =====================================
2448
<FORM method=POST action=$FORM>
2450
<table border width="100%" >
2452
<th>MySQL server</th>
2453
<th>User information</th>
2461
<td halign=right><b>Host</b><br><font size=-2>(Host on which MySQL-server resides.)</font></td>
2462
<td valign=top><INPUT name=rhost type=text size=15 maxlength=15 value="$MySQLaccess::Param{'rhost'}"></td>
2465
<td halign=right><b>Superuser</b><br><font size=-2>(User which has <font color="Red">read-access</font> to grant-tables.)</font></td>
2466
<td valign=top><INPUT name=superuser type=text size=15 maxlength=15 value="$MySQLaccess::Param{'superuser'}"></td>
2469
<td halign=right><b>Password</b><br><font size=-2>(of Superuser.)</font></td>
2470
<td valign=top><INPUT name=spassword type=password size=15 maxlength=15 value="$MySQLaccess::Param{'spassword'}"></td>
2478
<td halign=right><b><font color=Red>User</font></b><br><font size=-2>(Userid used to connect to MySQL-database.)</font></td>
2479
<td halign=top><INPUT name=user type=text size=15 maxlength=15 value="$MySQLaccess::Param{'user'}"></td>
2482
<td halign=right><b>Password</b><br><font size=-2>(Password user has to give to get access to MySQL-database.)</font></td>
2483
<td valign=top><INPUT name=password type=password size=15 maxlength=15 value="$MySQLaccess::Param{'password'}"></td>
2486
<td halign=right><b><font color=Red>Database</font></b><br><font size=-2>(Name of MySQL-database user tries to connect to.</font><br><font size=-2>Wildcards <font color="Green">(*,?,%,_)</font> are allowed.)</font></td>
2487
<td valign=top><INPUT name=db type=text size=15 maxlength=15 value="$MySQLaccess::Param{'db'}"></td>
2490
<td halign=right><b>Host</b><br><font size=-2>(Host from where the user is trying to connect to MySQL-database.</font><br><font size=-2>Wildcards <font color="Green">(*,?,%,_)</font> are allowed.)</font></td>
2491
<td valign=top><INPUT name=host type=text size=15 maxlength=15 value="$MySQLaccess::Param{'host'}"></td>
2497
<table cellspacing=5 cellpadding=2 cols=1 height="100%">
2499
<td halign=right><INPUT type=submit name=brief value="Brief"><br>
2500
<INPUT type=submit name=table value="Tabular"></td>
2506
<td halign=right><INPUT type=reset value="Clear"></td>
2522
# =====================================
2524
# print some information on STDOUT
2525
# =====================================
2527
Print_Error_Messages();
2528
if ($MySQLaccess::CMD) { #command-line mode
2531
if ($MySQLaccess::CGI) { #CGI-BIN mode
2537
# ======================================
2538
# sub Print_Version:
2539
# ======================================
2541
if ($MySQLaccess::CMD) {
2542
print $MySQLaccess::INFO;
2544
if ($MySQLaccess::CGI) {
2546
print $MySQLaccess::INFO;
2552
# ======================================
2553
# sub Print_Relnotes:
2554
# ======================================
2555
sub Print_Relnotes {
2556
if ($MySQLaccess::CMD) {
2557
print $MySQLaccess::RELEASE;
2559
if ($MySQLaccess::CGI) {
2561
print $MySQLaccess::RELEASE;
2567
# ======================================
2569
# ======================================
2571
if ($MySQLaccess::CMD) {
2572
print $MySQLaccess::TODO;
2574
if ($MySQLaccess::CGI) {
2576
print $MySQLaccess::TODO;
2582
# ======================================
2584
# ======================================
2586
if ($MySQLaccess::CMD) {
2587
print $MySQLaccess::HOWTO;
2589
if ($MySQLaccess::CGI) {
2591
print $MySQLaccess::HOWTO;
2597
# ======================================
2598
# sub Print_Options:
2599
# ======================================
2601
if ($MySQLaccess::CGI) { print "<PRE>\n"; }
2602
print $MySQLaccess::OPTIONS;
2603
if ($MySQLaccess::CGI) { print "</PRE>\n"; }
2607
# ======================================
2608
# sub Print_Error_Access:
2609
# ======================================
2610
sub Print_Error_Access {
2613
if ($MySQLaccess::CGI) { print "<font color=Red>\n<PRE>\n"; }
2614
print $MESSAGES{$error};
2615
if ($MySQLaccess::CGI) { print "</PRE>\n</font>\n"; }
2620
# ======================================
2621
# sub Print_Error_Messages:
2622
# ======================================
2623
sub Print_Error_Messages {
2626
if ($MySQLaccess::CGI) { print "<font color=Red>\n<center>\n"; }
2627
foreach $error (@MySQLaccess::Grant::Error) {
2628
print $MESSAGES{$error};
2629
print $MySQLaccess::CGI ? "<br>\n" : "\n";
2631
if ($MySQLaccess::CGI) { print "</center>\n</font>\n"; }
2636
# ======================================
2637
# sub Print_Message:
2638
# ======================================
2641
my @messages = @{$aref};
2643
if ($MySQLaccess::CGI) { print "<font color=DarkGreen>\n<center>\n"; }
2644
foreach $msg (@messages) {
2646
print $MySQLaccess::CGI ? "<br>\n" : "\n";
2648
if ($MySQLaccess::CGI) { print "</center>\n</font>\n"; }
2653
# ======================================
2655
# ======================================
2658
if (!$MySQLaccess::CGI) {
2659
print "Note: Editing the temporary tables is NOT supported in CMD-line mode!\n";
2663
."<form action=$FORM method=GET>\n"
2664
."<table width=90% border>\n"
2666
." <td><input type=checkbox name=copy value=on> Copy grant-rules to temporary tables<br></td>\n"
2667
." <td rowspan=5 align=center valign=center><input type=submit value=Go></td>\n"
2670
." <td> Edit temporary tables with external application:<br>"
2671
." <a href=\"$MySQLaccess::MYSQLADMIN\">$MySQLaccess::MYSQLADMIN</a></td>\n"
2674
." <td><input type=checkbox name=preview value=on> Preview changes made in temporary tables</td>\n"
2677
." <td><input type=checkbox name=commit value=on> Make changes permanent</td>\n"
2680
." <td><input type=checkbox name=rollback value=on> Restore previous grand-rules</td>\n"
2683
." <td colspan=2 align=center><font size=-2 color=Red>You need write,delete and drop-privileges to perform the above actions</font></td>\n"
2693
# ======================================
2694
# sub Print_Access_rights:
2695
# print the access-rights on STDOUT
2696
# ======================================
2697
sub Print_Access_rights {
2698
my ($host,$user,$db,$refhash) = @_;
2700
if (defined($MySQLaccess::Param{'brief'})) {
2701
# if ($MySQLaccess::CGI) { print "<PRE>\n"; }
2702
Matrix_Report($host,$user,$db,$refhash);
2703
# if ($MySQLaccess::CGI) { print "</PRE>\n"; }
2706
Tabular_Report($host,$user,$db,$refhash);
2707
$MySQLaccess::Report::separator = $MySQLaccess::CGI ? "<hr>" : "-"x80;
2712
# ======================================
2713
# sub Print_Diff_ACL:
2714
# print the diff. in the grants before and after
2715
# ======================================
2716
sub Print_Diff_ACL {
2718
my @diffs = @{$aref};
2719
my %block = ( '<' => 'Before',
2722
my %color = ( '<' => 'Green',
2727
# -----------------------------
2728
# create column-headers
2729
foreach $field (@MySQLaccess::Grant::Privileges) {
2730
push(@headers,substr($field,0,4));
2733
if ($MySQLaccess::CMD) {
2735
print "Differences in access-rights BEFORE and AFTER changes in grant-tables\n";
2736
# print "---------------------------------------------------------------------\n";
2739
$line1 .= sprintf("| %-30s|",'Host,User,DB');
2740
$line2 .= sprintf("+-%-30s+",'-' x 30);
2741
foreach $header (@headers) {
2742
$line1 .= sprintf("%-4s|",$header);
2743
$line2 .= sprintf("%s+",'----');
2749
$format = "format STDOUT = \n"
2750
. "^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< " . " @|||" x 10 ."\n"
2751
. '$host_user_db,@priv' . "\n"
2756
if ($MySQLaccess::CGI) {
2757
print "<table border width=100%>\n";
2759
print "<th colspan=11>";
2760
print "Differences in access-rights <font color=$color{'<'}>BEFORE</font> "
2761
."and <font color=$color{'>'}>AFTER</font> changes to grant-tables</font>\n";
2765
$line1 .= sprintf("<th>%-20s</th>",'Host, User, DB');
2766
foreach $header (@headers) {
2767
$line1 .= sprintf("<th>%-4s</th>",$header);
2769
print "$line1</tr>\n";
2772
foreach $line (@diffs) {
2773
$type = substr($line,0,1);
2774
$line = substr($line,1);
2775
($host,$user,$db,@priv) = split(/,/,$line);
2776
if ($MySQLaccess::CMD) {
2777
if ($type ne $curblock) {
2779
print $block{$curblock},":\n";
2784
if ($MySQLaccess::CGI) {
2785
if ($type ne $curblock) {
2787
print "<tr><td><b>$block{$curblock}<b></td></tr>\n";
2789
$line1="<td><font color=$color{$type}>$host, $user, $db</font></td>";
2790
foreach $field (@priv) {
2791
$line1 .= sprintf("<td align=center><font color=$color{$type}>%-4s</font></td>",$field);
2793
print "<tr>$line1</tr>\n";
2797
if ($MySQLaccess::CMD) {
2798
print "---------------------------------------------------------------------\n";
2800
if ($MySQLaccess::CGI) {
2801
print "</table><br>";
2808
# ======================================
2809
# sub Tabular_Report
2811
# suitable for 1 triple (host,db,user)
2812
# ======================================
2813
sub Tabular_Report {
2814
my ($host,$user,$db,$a) = @_;
2817
# -----------------------------
2819
if ($MySQLaccess::Report::separator) { print "$MySQLaccess::Report::separator\n"; }
2821
# -----------------------------
2822
# print table of access-rights
2823
my $rows = int(@MySQLaccess::Grant::Privileges/2); #round up
2826
for $i (0 .. $rows-1) {
2827
$table[$j]=$MySQLaccess::Grant::Privileges[$i];
2831
for $i ($rows .. $#MySQLaccess::Grant::Privileges) {
2832
$table[$j]=$MySQLaccess::Grant::Privileges[$i];
2835
if ($MySQLaccess::CMD) {
2837
print "Access-rights\n";
2838
print "for USER '$user', from HOST '$host', to DB '$db'\n";
2840
if ($MySQLaccess::CGI) {
2841
print "<table border width=100%>\n";
2844
if ($MySQLaccess::CGI) {
2845
print "<th colspan=5>";
2846
print "<font color=Red>Access-rights</font>\n";
2847
print "for USER '<font color=Green>$user</font>', from HOST '<font color=Green>$host</font>', to DB '<font color=Green>$db</font>'\n";
2852
if ($MySQLaccess::CMD) {
2853
print "\t+-----------------+---+\t+-----------------+---+";
2855
foreach $field (@table) {
2856
if ($MySQLaccess::CMD) {
2857
if ($column==2) { print "\n\t"; $column=1;}
2858
else { print "\t"; $column=2;}
2859
printf "| %-15s | %s |",$field,$Answer{$a->{$field}};
2861
if ($MySQLaccess::CGI) {
2862
if ($column==2) { print "</tr>\n<tr>\n"; $column=1;}
2863
else { print "<td width=10%></td>"; $column=2;}
2864
printf " <td width=35%><b>%-15s</b></td><td width=10%>%s</td>\n",$field,$Answer{$a->{$field}};
2868
if ($MySQLaccess::CMD) {
2869
print "\t+-----------------+---+\t+-----------------+---+\n";
2871
if ($MySQLaccess::CGI) {
2872
print "</tr>\n</table><br>";
2877
foreach $note (@MySQLaccess::Grant::Notes) {
2878
my $message = $MESSAGES{$note};
2879
$message =~ s/\$user/$user/g;
2880
$message =~ s/\$db/$db/g;
2881
$message =~ s/\$host/$host/g;
2882
$message =~ s/\$password/$password/g;
2884
if ($MySQLaccess::CMD) {
2885
my @lines = split(/\n/,$message);
2886
foreach $line (@lines) {
2887
print "$PREFIX:\t $line\n";
2891
if ($MySQLaccess::CGI) {
2892
print "<b>$PREFIX:</b> $message<br>\n";
2898
foreach $warning (@MySQLaccess::Grant::Warnings) {
2899
my $message = $MESSAGES{$warning};
2900
$message =~ s/\$user/$user/g;
2901
$message =~ s/\$db/$db/g;
2902
$message =~ s/\$host/$host/g;
2903
$message =~ s/\$password/$password/g;
2905
if ($MySQLaccess::CMD) {
2906
my @lines = split(/\n/,$message);
2907
foreach $line (@lines) {
2908
print "$PREFIX:\t $line\n";
2912
if ($MySQLaccess::CGI) {
2913
print "<b>$PREFIX:</b> $message<br>\n";
2919
foreach $error (@MySQLaccess::Grant::Errors) {
2920
my $message = $MESSAGES{$error};
2921
$message =~ s/\$user/$user/g;
2922
$message =~ s/\$db/$db/g;
2923
$message =~ s/\$host/$host/g;
2924
$message =~ s/\$password/$password/g;
2926
if ($MySQLaccess::CMD) {
2927
my @lines = split(/\n/,$message);
2928
foreach $line (@lines) {
2929
print "$PREFIX:\t $line\n";
2933
if ($MySQLaccess::CGI) {
2934
print "<b>$PREFIX:</b> $message<br>\n";
2939
# inform if there are no rules ==> full access for everyone.
2940
if ($MySQLaccess::Grant::full_access) { print "$MESSAGES{'full_access'}\n"; }
2943
# print the rules used
2945
if ($MySQLaccess::CMD) {
2946
print "The following rules are used:\n";
2947
foreach $field (sort(keys(%MySQLaccess::Grant::Rules))) {
2948
my $rule = (defined($MESSAGES{$MySQLaccess::Grant::Rules{$field}}) ? $MESSAGES{$MySQLaccess::Grant::Rules{$field}} : $MySQLaccess::Grant::Rules{$field});
2949
$rule =~ s/\t/','/g;
2950
printf " %-5s : '%s'\n",$field,$rule;
2953
if ($MySQLaccess::CGI) {
2955
print "<table border width=100%>\n";
2956
print "<tr><th colspan=2>The following rules are used:</th></tr>\n";
2957
foreach $field (sort(keys(%MySQLaccess::Grant::Rules))) {
2958
my $rule = (defined($MESSAGES{$MySQLaccess::Grant::Rules{$field}}) ? $MESSAGES{$MySQLaccess::Grant::Rules{$field}} : $MySQLaccess::Grant::Rules{$field});
2959
$rule =~ s/\t/','/g;
2960
printf "<tr><th>%-5s</th><td>'%s'</td></tr>\n",$field,$rule;
2968
# ======================================
2969
# sub Matrix_Report:
2970
# single-line output foreach triple,
2971
# no notes,warnings,...
2972
# ======================================
2974
my ($host,$user,$db,$a) = @_;
2978
# -----------------------------
2979
# create column-headers
2980
foreach $field (@MySQLaccess::Grant::Privileges) {
2981
push(@headers,substr($field,0,4));
2984
# -----------------------------
2985
# print column-headers
2987
if ($MySQLaccess::CMD) {
2990
foreach $header (@headers) {
2991
$line1 .= sprintf("%-4s ",$header);
2992
$line2 .= sprintf("%s ",'----');
2994
$line1 .= sprintf("| %-20s",'Host,User,DB');
2995
$line2 .= sprintf("+ %-20s",'-' x 20);
2999
if ($MySQLaccess::CGI) {
3000
print "<table width=100% border>\n";
3002
foreach $header (@headers) {
3003
$line1 .= sprintf("<th>%-4s</th>",$header);
3005
$line1 .= sprintf("<th>%-20s</th>",'Host, User, DB');
3006
print "$line1</tr>\n";
3009
# ----------------------------
3010
# column-headers should only be
3012
$MySQLaccess::Report::headers=1;
3015
# ------------------------
3016
# print access-information
3017
if ($MySQLaccess::CMD) {
3018
foreach $field (@MySQLaccess::Grant::Privileges) {
3019
printf " %-2s ",$Answer{$a->{$field}};
3021
printf "| %-20s",join(',',$host,$user,$db);
3024
if ($MySQLaccess::CGI) {
3026
foreach $field (@MySQLaccess::Grant::Privileges) {
3027
printf "<td align=center>%-2s</td>",$Answer{$a->{$field}};
3029
printf "<td><b>%-20s</b></td>",join(', ',$host,$user,$db);
3037
# ======================================
3039
# single-line output foreach triple,
3040
# no notes,warnings,...
3041
# ======================================
3043
my ($host,$user,$db,$a) = @_;
3047
# ------------------------
3048
# print access-information
3049
$string = "$host,$user,$db,";
3050
foreach $field (@MySQLaccess::Grant::Privileges) {
3051
$string .= $Answer{$a->{$field}} . ",";
3057
#######################################################################
3058
package MySQLaccess::Wildcards;
3061
$DEBUG = $MySQLaccess::DEBUG unless ($DEBUG);
3063
# ############################################
3064
# SQL, WILDCARDS and REGULAR EXPRESSIONS
3065
# ============================================
3066
# translage SQL-expressions to Reg-expressions
3067
# ============================================
3071
$expr =~ s/\./\\./g;
3072
$expr =~ s/\\%/\002/g;
3074
$expr =~ s/\002/%/g;
3075
$expr =~ s/\\_/\002/g;
3077
$expr =~ s/\002/_/g;
3078
MySQLaccess::Debug::Print(2,"$expr_o --> $expr");
3082
# translage WILDcards to Reg-expressions
3083
# ============================================
3087
$expr =~ s/\./\\./g;
3088
$expr =~ s/\\\*/\002/g;
3090
$expr =~ s/\002/*/g;
3091
$expr =~ s/\\\?/\002/g;
3093
$expr =~ s/\002/?/g;
3094
MySQLaccess::Debug::Print(2,"$expr_o --> $expr");
3098
# =============================================
3099
# match a given string with a template
3100
# =============================================
3102
my ($tpl,$string) = @_;
3104
if ($string=~ /^$tpl$/ or $tpl eq '') { $match=1; }
3106
MySQLaccess::Debug::Print(2,"($tpl,$string) --> $match");
3110
#######################################################################
3111
package MySQLaccess::Host;
3115
$DEBUG = $MySQLaccess::DEBUG unless ($DEBUG);
3117
# ======================================
3119
# return the Name with the corr. IP-nmbr
3120
# (no aliases yet!!)
3121
# ======================================
3125
if ($ip !~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/o) {
3126
MySQLaccess::Debug::Print(3,"'$ip' is not an ip-number, returning IP=$ip");
3129
MySQLaccess::Debug::Print(4,"IP=$ip split up => $1.$2.$3.$4");
3130
$ip = pack "C4",$1,$2,$3,$4;
3131
MySQLaccess::Debug::Print(4,"IP packed -> >>$ip<<\n");
3132
my ($name,$aliases,$addrtype,$length,@addrs) = gethostbyaddr($ip, AF_INET);
3133
MySQLaccess::Debug::Print(3,"IP=$ip_o => hostname=$name");
3134
MySQLaccess::Debug::Print(4,"aliases=$aliases");
3135
MySQLaccess::Debug::Print(4,"addrtype=$addrtype - length=$length");
3136
return ($name || $ip);
3137
#return ($name || undef);
3140
# ======================================
3142
# return the IP-number of the host
3143
# ======================================
3146
if ($name =~ /[%_]/) {
3147
MySQLaccess::Debug::Print(3,"'$name' contains SQL-wildcards, returning name=$name");
3150
my ($_name,$aliases,$addrtype,$length,@addrs) = gethostbyname($name);
3151
my ($a,$b,$c,$d) = unpack('C4',$addrs[0]);
3152
my $ip = "$a.$b.$c.$d";
3153
MySQLaccess::Debug::Print(3,"hostname=$name => IP=$ip");
3154
MySQLaccess::Debug::Print(4,"aliases=$aliases");
3155
MySQLaccess::Debug::Print(4,"addrtype=$addrtype - length=$length");
3156
#if ($ip ne "") { return "$ip"; }
3157
#else { return undef; }
3158
return ($ip || $name);
3161
# ========================================
3163
# some special action has to be taken for
3165
# ========================================
3167
if (!defined($MySQLaccess::Host::localhost)) {
3168
$MySQLaccess::Host::localhost = Sys::Hostname::hostname();
3169
MySQLaccess::Debug::Print(3,"Setting package variable \$localhost=$MySQLaccess::Host::localhost");
3171
my $host = $localhost;
3172
MySQLaccess::Debug::Print(3,"localhost = $host");
3176
# ========================================
3177
# check if the given hostname (or ip)
3178
# corresponds with the localhost
3179
# ========================================
3181
my ($host_tpl) = @_;
3183
if (($MySQLaccess::host_name eq $localhost) or ($MySQLaccess::host_ip eq $local_ip)) {
3184
MySQLaccess::Debug::Print(2,"Checking for localhost");
3185
MySQLaccess::Debug::Print(3,"because ($MySQLaccess::host_name EQ $localhost) AND ($MySQLaccess::host_ip EQ $local_ip)");
3186
$isit = ( 'localhost' =~ /$host_tpl/ ) ? 1 : 0;
3187
MySQLaccess::Debug::Print(3," 'localhost' =?= $host_tpl -> $isit");
3191
MySQLaccess::Debug::Print(4,"Not checking for localhost");
3192
MySQLaccess::Debug::Print(4,"because ($MySQLaccess::host_name != $localhost) AND ($MySQLaccess::host_ip != $local_ip)");
3198
# =========================================
3199
# check if host (IP or name) can be matched
3201
# =========================================
3203
my ($host,$tpl) = @_;
3206
MySQLaccess::Debug::Print(1, "($host) =?= ($tpl)");
3208
my $host_name = IP2Name($host);
3209
my $host_ip = Name2IP($host);
3211
MySQLaccess::Debug::Print(2, "name=$host_name ; ip=$host_ip");
3212
$match = (MySQLaccess::Wildcards::MatchTemplate($tpl,$host_name) or
3213
MySQLaccess::Wildcards::MatchTemplate($tpl,$host_ip));
3215
MySQLaccess::Debug::Print(2, "($host_name,$host_ip) =?= ($tpl): $ncount");
3220
########################################################################
3221
package MySQLaccess::Debug;
3223
my $dbg_file = "$MySQLaccess::script_log";
3224
open(DEBUG,"> $dbg_file") or warn "Could not open outputfile $dbg_file for debugging-info\n";
3229
# =========================================
3230
# Print debugging information on STDERR
3231
# =========================================
3233
my ($level,$mesg) = @_;
3234
my ($pack,$file,$line,$subname,$hasargs,$wantarray) = caller(1);
3235
my ($PACK) = split('::',$subname);
3236
my $DEBUG = ${$PACK."::DEBUG"} ? ${$PACK."::DEBUG"} : $MySQLaccess::DEBUG ;
3237
my ($sec,$min,$hour) = localtime();
3238
print DEBUG "[$hour:$min:$sec $subname] $mesg\n" if ($DEBUG>=$level);