-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
Copy pathmysqlaccess.sh
3267 lines (2897 loc) · 110 KB
/
mysqlaccess.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!@PERL_PATH@
# Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1335 USA
# ****************************
package MySQLaccess;
#use strict;
use File::Temp qw(tempfile tmpnam);
use Fcntl;
BEGIN {
# ****************************
# static information...
$VERSION = "2.10, 13 Sep 2019";
$0 =~ m%/([^/]+)$%o;
$script = $1;
$script = 'MySQLAccess' unless $script;
$script_conf = "$script.conf";
$script_log = $ENV{'HOME'}."/$script.log";
warn "$0: Deprecated program name. It will be removed in a future release, use 'mariadb-access' instead\n"
if $0 =~ m/mysqlaccess$/;
# ****************************
# information on MariaDB
$MYSQL = '@bindir@/mariadb'; # path to mariadb executable
$SERVER = '3.21';
$MYSQL_OPT = ' --batch --unbuffered';
$ACCESS_DB = 'mysql'; # name of DB with grant-tables
$ACCESS_H = 'host'; #
$ACCESS_U = 'user'; #
$ACCESS_D = 'db'; #
# Add/Edit privileges
$ACCESS_H_TMP = 'host_tmp';
$ACCESS_U_TMP = 'user_tmp';
$ACCESS_D_TMP = 'db_tmp';
$ACCESS_H_BCK = 'host_backup';
$ACCESS_U_BCK = 'user_backup';
$ACCESS_D_BCK = 'db_backup';
$DIFF = '/usr/bin/diff';
$MYSQLDUMP = '@bindir@/mariadb-dump';
#path to mariadb-dump executable
$MYSQLADMIN= 'http://foobar.com/MySQLadmin';
#URL of CGI for manipulating
#the temporary grant-tables
}
END {
unlink $MYSQL_CNF if defined $MYSQL_CNF and not $DEBUG;
}
$INFO = <<"_INFO";
--------------------------------------------------------------------------
mysqlaccess (Version $VERSION)
~~~~~~~~~~~
Copyright (C) 1997,1998 Yves.Carlier\@rug.ac.be
University of Ghent (RUG), Belgium
Administratieve Informatieverwerking (AIV)
report the access-privileges for a USER from a HOST to a DB
Many thanks go to <monty\@mysql.com> and <psmith\@BayNetworks.COM>
for their suggestions, debugging and patches.
use `$script -?' to get more information on available options.
From version 2.0x, $script can also be used through a WEB-browser
if it is ran as a CGI-script. (See the release-notes)
--------------------------------------------------------------------------
_INFO
$OPTIONS = <<_OPTIONS;
Usage: $script [host [user [db]]] OPTIONS
-?, --help display this helpscreen and exit
-v, --version print information on the program `$script'
-u, --user=# username for logging in to the db
-p, --password=# validate password for user
-h, --host=# name or IP-number of the host
-d, --db=# name of the database
-U, --superuser=# connect as superuser
-P, --spassword=# password for superuser
-H, --rhost=# remote MariaDB-server to connect to
--old_server connect to old MariaDB-server (before v3.21) which
does not yet know how to handle full where clauses.
-b, --brief single-line tabular report
-t, --table report in table-format
--relnotes print release-notes
--plan print suggestions/ideas for future releases
--howto some examples of how to run `$script'
--debug=N enter debuglevel N (0..3)
--copy reload temporary grant-tables from original ones
--preview show differences in privileges after making
changes in (temporary) grant-tables
--commit copy grant-rules from temporary tables to grant-tables
(!don't forget to do an mysqladmin reload)
--rollback undo the last changes to the grant-tables.
Note:
At least the user and the db must be given (even with wildcards)
If no host is given, `localhost' is assumed
Wilcards (*,?,%,_) are allowed for host, user and db, but be sure
to escape them from your shell!! (ie type \\* or '*')
_OPTIONS
$RELEASE = <<'_RELEASE';
Release Notes:
-------------
0.1-beta1: internal
- first trial.
0.1-beta2: (1997-02-27)
- complete rewrite of the granting-rules, based on the documentation
found in de FAQ.
- IP-number and name for a host are equiv.
0.1-beta3: (1997-03-10)
- more information
- 'localhost' and the name/ip of the local machine are now equiv.
0.1-beta4: (1997-03-11)
- inform the user if he has not enough priv. to read the mysql db
1.0-beta1: (1997-03-12)
suggestions by Monty:
- connect as superuser with superpassword.
- mysqlaccess could also notice if all tables are empty. This means
that all user have full access!
- It would be nice if one could optionally start mysqlaccess without
any options just the arguments 'user db' or 'host user db', where
host is 'localhost' if one uses only two arguments.
1.0-beta2: (1997-03-14)
- bugfix: translation to reg.expr of \_ and \%.
- bugfix: error in matching regular expression and string given
by user which resulted in
'test_123' being matched with 'test'
1.0-beta3: (1997-03-14)
- bugfix: the user-field should not be treated as a sql-regexpr,
but as a plain string.
- bugfix: the host-table should not be used if the host isn't empty in db
or if the host isn't emty in user
(Monty)
1.0-beta4: (1997-03-14)
- bugfix: in an expression "$i = $j or $k", the '=' binds tighter than the or
which results in problems...
(by Monty)
- running mysqlaccess with "perl -w" gives less warnings... ;-)
1.0-beta5: (1997-04-04)
- bugfix: The table sorting was only being applied to the "user" table; all
the tables need to be sorted. Rewrote the sort algorithm, and
the table walk algorithm (no temp file anymore), and various
other cleanups. I believe the access calculation is 100% correct.
(by Paul D. Smith <psmith\@baynetworks.com>)
- Allow the debug level to be set on the cmd line with --debug=N.
(by Paul D. Smith <psmith\@baynetworks.com>)
- More -w cleanups; should be totally -w-clean.
(by Paul D. Smith <psmith\@baynetworks.com>)
1.1-beta1: (1997-04-xx)
1.1-beta2: (1997-04-11)
- new options:
--all_users : report access-rights for all possible users
--all_dbs : report access-rights for all possible dbs
--all_hosts : report access-rights for all possible hosts
--brief : as brief as possible, don't mention notes,warnings and rules
--password : validate password for user
- layout: long messages are wrapped on the report.
- functionality:
more descriptive notes and warnings
wildcards (*,?) are allowed in the user,host and db options
setting xxxx=* is equiv to using option --all_xxxx
note: make sure you escape your wildcards, so they don't get
interpreted by the shell. use \* or '*'
- bugfix: Fieldnames which should be skipped on the output can now have
a first capital letter.
- bugfix: any option with a '.' (eg ip-number) was interpreted as
a wildcard-expression.
- bugfix: When no entry was found in the db-table, the default accessrights are
N, instead of the faulty Y in a previous version.
1.1-beta-3 : (1997-04-xx)
1.1-beta-4 : (1997-04-xx)
1.1-beta-5 : (1997-04-xx)
1.1 : (1997-04-28)
- new options:
--rhost : name of mysql-server to connect to
--plan : print suggestions/ideas for future releases
--relnotes : display release-notes
--howto : display examples on how to use mysqlaccess
--brief : single-line tabular output
- functionality/bugfix:
* removed options --all_users,--all_dbs,--all_hosts, which
were redundant with the wildcard-expressions for the corresponding
options. They made the processing of the commandline too painful
and confusing ;-)
(suggested by psmith)
* redefined the option --brief, which now gives a single-line
tabular output
* Now we check if the right version of the mysql-client is used,
since we might use an option not yet implemented in an
older version (--unbuffered, since 3.0.18)
Also the error-messages the mysql-client reports are
better interpreted ;-)
* Wildcards can now be given following the SQL-expression
(%,_) and the Regular-expression (*,?) syntax.
- speed: we now open a bidirectional pipe to the mysql-client, and keep
it open throughout the whole run. Queries are written to,
and the answers read from the pipe.
(suggested by monty)
- bugfixes:
* the Rules were not properly reset over iterations
* when in different tables the field-names were not identical,
eg. Select_priv and select_priv, they were considered as
definitions of 2 different access-rights.
* the IP-number of a host with a name containing wildcards should
not be searched for in Name2IP and IP2Name.
* various other small things, pointed out by <monty> and <psmith>
1.2 : (1997-05-13)
- bugfix:
* Fixed bug in acl with anonymous user: Now if one gets accepted by the
user table as a empty user name, the user name is set to '' when
checking against the 'db' and 'host' tables. (Bug fixed in MySQL3.20.19)
1.2-1 : (1997-xx-xx)
- bugfix:
* hashes should be initialized with () instead of {} <psmith>
* "my" variable $name masks earlier declaration in same scope,
using perl 5.004 <????>
1.2-2 : (1997-06-10)
2.0p1-3 : (1997-10-xx)
- new
* packages
* log-file for debug-output : /tmp/mysqlaccess.log
* default values are read from a configuration file $script.conf
first this file is looked for in the current directory; if not
found it is looked for in @sysconfdir@
Note that when default-values are given, these can't get overridden
by empty (blanc) values!
* CGI-BIN version with HTML and forms interface. Simply place the
script in an ScriptAliased directory, make the configuration file
available in the that directory or in @sysconfdir@, and point your browser
to the right URL.
* copy the grant-rules to temporary tables, where you are safe to
play with them.
* preview changes in privileges after changing grant-rules,
before taking them into production
* copy the new grant-rules from the temporary tables back to the
grant-tables.
* Undo all changes made in the grant-tables (1-level undo).
-new options:
* --table : as opposite of the --brief option.
* --copy : (re)load temporary grant-tables from original ones.
* --preview : preview changes in privileges after changing
some or more entries in the grant-tables.
* --commit : copy grant-rules from temporary tables to grant-tables
(!don't forget to do an mysqladmin reload)
* --rollback: undo the last changes to the grant-tables.
- bugfix:
* if the table db is empty, mysqlaccess freezed
(by X Zhu <[email protected]>)
2.0 : (1997-10-09)
- fixed some "-w" warnings.
- complain when certain programs and paths can't be found.
2.01 : (1997-12-12)
- bugfix:
* rules for db-table where not calculated and reported correctly.
2.02 : (1998-01-xx)
- bugfix:
* Privileges of the user-table were not AND-ed properly with the
other privileges. (reported by monty)
- new option:
* --old_server: mysqlaccess will now use a full where clause when
retrieving information from the MySQL-server. If
you are connecting to an old server (before v3.21)
then use the option --old_server.
2.03 : (1998-02-27)
- bugfix:
* in Host::MatchTemplate: incorrect match if host-field was left empty.
2.04-alpha1 : (2000-02-11)
Closes vulnerability due to former implementation requiring passwords
to be passed on the command line.
- functionality
Option values for --password -p -spassword -P may now be omitted from
command line, in which case the values will be prompted for.
(fix supplied by Steve Harvey <[email protected]>)
2.05: (2000-02-17) Monty
Moved the log file from /tmp to ~
2.06: Don't print '+++USING FULL WHERE CLAUSE+++'
_RELEASE
$TODO = <<_TODO;
Plans:
-----
-a full where clause is use now. How can we handle older servers?
-add some more functionality for DNS.
-select the warnings more carefuly.
>> I think that the warnings should either be enhanced to _really_
>> understand and report real problems accurately, or restricted to
>> only printing things that it knows with 100% certainty. <psmith)
>> Why do I have both '%' and 'any_other_host' in there? Isn't that
>> the same thing? I think it's because I have an actual host '%' in
>> one of my tables. Probably the script should catch that and not
>> duplicate output. <psmith>
_TODO
# From the FAQ: the Grant-algorithm
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The host table is mainly to maintain a list of "secure" servers.
# At TCX hosts contain a list of all machines on local network. These are granted
# all privileges.
# Technically the user grant is calculated by:
#
# 1.First sort all entries by host by putting host without wildcards first,
# after this host with wildcards and entries with host = ".
# Under each host sort user by the same criterias.
# 2.Get grant for user from the "db" table.
# 3.If hostname is "empty" for the found entry, AND the privileges with
# the privileges for the host in "host" table.
# (Remove all which is not "Y" in both)
# 4.OR (add) the privileges for the user from the "user" table.
# (add all privileges which is "Y" in "user")
#
# When matching, use the first found match.
#
# -----------------------------------------------------------------------------------
$HOWTO = <<_HOWTO;
Examples of how to call $script:
~~~~~~~~
1)Calling $script with 2 arguments:
\$ $script root mysql
->report rights of user root logged on at the local host in db mysql
Access-rights
for USER 'root', from HOST 'localhost', to DB 'mysql'
+-----------------+---+ +-----------------+---+
| select_priv | Y | | drop_priv | Y |
| insert_priv | Y | | reload_priv | Y |
| update_priv | Y | | shutdown_priv | Y |
| delete_priv | Y | | process_priv | Y |
| create_priv | Y | | file_priv | Y |
+-----------------+---+ +-----------------+---+
BEWARE: Everybody can access your DB as user 'root'
: WITHOUT supplying a password. Be very careful about it!!
The following rules are used:
db : 'No matching rule'
host : 'Not processed: host-field is not empty in db-table.'
user : 'localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y'
2)Calling $script with 3 arguments:
\$ $script foo.bar nobody Foo
->report rights of user root logged in at machine foobar to db Foo
Access-rights
for USER 'nobody', from HOST 'foo.bar', to DB 'Foo'
+-----------------+---+ +-----------------+---+
| select_priv | Y | | drop_priv | N |
| insert_priv | Y | | reload_priv | N |
| update_priv | Y | | shutdown_priv | N |
| delete_priv | Y | | process_priv | N |
| create_priv | N | | file_priv | N |
+-----------------+---+ +-----------------+---+
BEWARE: Everybody can access your DB as user 'nobody'
: WITHOUT supplying a password. Be very careful about it!!
The following rules are used:
db : 'foo.bar','Foo','nobody','Y','Y','Y','N','N','N'
host : 'Not processed: host-field is not empty in db-table.'
user : 'foo.bar','nobody','','N','N','N','Y','N','N','N','N','N','N'
3)Using wildcards:
\$ $script \\* nobody Foo --brief
->report access-rights of user nobody from all machines to db Foo,
and use a matrix-report.
Sel Ins Upd Del Crea Drop Reld Shut Proc File Host,User,DB
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --------------------
Y Y Y Y N N N N N N localhost,nobody,Foo
N N N N N N N N N N %,nobody,Foo
N N N N N N N N N N any_other_host,nobody,Foo
_HOWTO
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
# START OF THE PROGRAM #
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
use Getopt::Long;
use Sys::Hostname;
use IPC::Open3;
# ****************************
# debugging flag
# can be set to 0,1,2,3
# a higher value gives more info
# ! this can also be set on the command-line
$DEBUG = 0;
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>8
# Normally nothing should be changed beneeth this line
# ****************************
# no caching on STDOUT
$|=1;
$MYSQL_CNF = tmpnam();
%MYSQL_CNF = (client => { },
mysql => { },
mysqldump => { },
);
$NEW_USER = 'ANY_NEW_USER';
$NEW_DB = 'ANY_NEW_DB' ;
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #
# mysqlaccess: #
# ~~~~~~~~~~~ #
# Lets get to it, #
# and start the program by processing the parameters #
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #
($CMD,$CGI) = GetMode();
# ****************************
# the copyright message should
# always be printed (once)
MySQLaccess::Report::Print_Header();
# *****************************
# Read configuration-file
MySQLaccess::Debug::Print(1, "Reading configuration file...");
if (-f "@sysconfdir@/$script_conf") {
print "Configuration file '$script_conf' is found in '@sysconfdir@/'\n";
require "@sysconfdir@/$script_conf";
}
elsif (-f "@prefix@/$script_conf") {
print "Configuration file '$script_conf' is found in '@prefix@/'\n";
require "@prefix@/$script_conf";
}
elsif (-f "./$script_conf") {
print "\nERROR! Configuration file '$script_conf' is found in the current ";
print "directory.\nThe permissible locations for this file are either ";
print "@sysconfdir@/ or @prefix@/\n";
print "Please move it to one of these locations and retry.\n\n";
exit 0;
}
# ****************************
# Read in all parameters
if ($MySQLaccess::CMD) { #command-line version
# ----------------------------
# Get options from commandline
$Getopt::Long::ignorecase=0; #case sensitive options
if ( grep(/\-\?/,@ARGV) ) { MySQLaccess::Report::Print_Usage(); exit 0; }
GetOptions("help" => \$Param{'help'}
,"host|h=s" => \$Param{'host'}
,"user|u=s" => \$Param{'user'}
,"password|p:s" => \$Param{'password'}
,"db|d=s" => \$Param{'db'}
,"superuser|U=s" => \$Param{'superuser'}
,"spassword|P:s" => \$Param{'spassword'}
,"rhost|H=s" => \$Param{'rhost'}
,"old_server" => \$Param{'old_server'}
,"debug=i" => \$Param{'DEBUG'}
,"brief|b" => \$Param{'brief'}
,"table|t" => \$Param{'table'}
,"relnotes" => \$Param{'relnotes'}
,"plan" => \$Param{'plan'}
,"howto" => \$Param{'howto'}
,"version|v" => \$Param{'version'}
,"preview" => \$Param{'preview'}
,"copy" => \$Param{'copy'}
,"commit" => \$Param{'commit'}
,'rollback' => \$Param{'rollback'}
);
# -----------------------------
# set DEBUG
$DEBUG = $Param{'DEBUG'} if ($Param{'DEBUG'}>=$DEBUG);
# -----------------------------
# check for things which aren't
# declared as options:
# 2 arguments: (user,db) -> ('localhost','user','db')
if ($#ARGV == 1) {
MySQLaccess::Debug::Print(2,"$script called with 2 arguments:");
$Param{'host'} = $Param{'host'} || 'localhost';
$Param{'user'} = $ARGV[0] || $Param{'user'};
$Param{'db'} = $ARGV[1] || $Param{'db'};
}
# 3 arguments: (host,user,db)
if ($#ARGV == 2) {
MySQLaccess::Debug::Print(2,"$script called with 3 arguments:");
$Param{'host'} = $ARGV[0] || $Param{'host'};
$Param{'user'} = $ARGV[1] || $Param{'user'};
$Param{'db'} = $ARGV[2] || $Param{'db'};
}
# -------------------------------------
# prompt for user password if requested
if ( defined($Param{'password'}) && length($Param{'password'}) == 0 ) {
$Param{'password'} = PromptPass(
"Password for MySQL user $Param{'user'}: ");
}
}
if ($MySQLaccess::CGI) { #CGI-version
require CGI;
$Q = new CGI;
$Param{'help'} = $Q->param('help') ;
$Param{'host'} = $Q->param('host') || $Q->param('h') || $Param{'host'};
$Param{'user'} = $Q->param('user') || $Q->param('u') || $Param{'user'};
$Param{'db'} = $Q->param('db') || $Q->param('d') || $Param{'db'};
$Param{'password'} = $Q->param('password') || $Q->param('p') || $Param{'password'};
$Param{'superuser'} = $Q->param('superuser') || $Q->param('U') || $Param{'superuser'};
$Param{'spassword'} = $Q->param('spassword') || $Q->param('P') || $Param{'spassword'};
$Param{'rhost'} = $Q->param('rhost') || $Q->param('H') || $Param{'rhost'};
$Param{'old_server'}= $Q->param('old_server')|| $Param{'old_server'};
$Param{'debug'} = $Q->param('debug') || $Param{'debug'};
$Param{'brief'} = $Q->param('brief') || $Param{'brief'};
$Param{'table'} = $Q->param('table') || $Param{'table'};
$Param{'relnotes'} = $Q->param('relnotes');
$Param{'plan'} = $Q->param('plan');
$Param{'howto'} = $Q->param('howto');
$Param{'version'} = $Q->param('version') ? $Q->param('version') : $Q->param('v');
$Param{'edit'} = $Q->param('edit');
$Param{'preview'} = $Q->param('preview');
$Param{'copy'} = $Q->param('copy');
$Param{'commit'} = $Q->param('commit');
$Param{'rollback'} = $Q->param('rollback');
# -----------------------------
# set DEBUG
$DEBUG = $Q->param('debug') if ($Q->param('debug')>=$DEBUG);
}
# ----------------------
# brief and table-format
# exclude each-other
# table-format is preferred
if (defined($Param{'table'})) { undef($Param{'brief'}); }
if (defined($Param{'preview'}) or
defined($Param{'copy'}) or
defined($Param{'commit'}) or
defined($Param{'rollback'}) ) { $Param{'edit'}='on'; }
# ----------------------
# if no host is given
# assume we mean 'localhost'
if (!defined($Param{'host'})) { $Param{'host'}='localhost'; }
# ----------------------
# perform some checks
# -> eliminate 'broken pipe' error
push(@MySQLaccess::Grant::Error,'not_found_mysql') if !(-x $MYSQL);
push(@MySQLaccess::Grant::Error,'not_found_diff') if !(-x $DIFF);
push(@MySQLaccess::Grant::Error,'not_found_mysqldump') if !(-x $MYSQLDUMP);
if (@MySQLaccess::Grant::Error) {
MySQLaccess::Report::Print_Error_Messages() ;
exit 0;
}
#-----------------------
# get info/help if necc.
$print_usage=1;
if ( defined($Param{'version'}) ) {
MySQLaccess::Report::Print_Version();
$print_usage=0;
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
# exit 0;
}
if ( defined($Param{'relnotes'}) ) {
MySQLaccess::Report::Print_Relnotes();
$print_usage=0;
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
# exit 0;
}
if ( defined($Param{'plan'}) ) {
MySQLaccess::Report::Print_Plans();
$print_usage=0;
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
# exit 0;
}
if ( defined($Param{'howto'}) ) {
MySQLaccess::Report::Print_HowTo();
$print_usage=0;
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
# exit 0;
}
# -----------------------------
# generate a help-screen in CMD-mode
# or a blanc form in CGI-mode
if ( defined($Param{'help'})
or !defined($Param{'user'})
or !defined($Param{'host'})
or !defined($Param{'db'})
) {
push(@MySQLaccess::Grant::Error,'user_required') unless defined($Param{'user'});
push(@MySQLaccess::Grant::Error,'db_required') unless defined($Param{'db'});
push(@MySQLaccess::Grant::Error,'host_required') unless defined($Param{'host'});
MySQLaccess::Report::Print_Usage() if $print_usage;
exit 0;
}
# ----------------------------
# get hostname and local-ip
# for localhost
$localhost = MySQLaccess::Host::LocalHost();
$local_ip = MySQLaccess::Host::Name2IP($localhost);
$MySQLaccess::Host::localhost = MySQLaccess::Host::LocalHost();
$MySQLaccess::Host::local_ip = MySQLaccess::Host::Name2IP($localhost);
MySQLaccess::Debug::Print(3, "localhost name=$localhost, ip=$local_ip");
#-----------------------------------
# version of MySQL-server to connect
# to determine use of full where clause
$MySQLaccess::Host::SERVER = $Param{'old_server'} ? '3.20' : $SERVER;
#---------------------------------
# create the config file for mysql and mysqldump
# to avoid passing authentication info on the command line
#
MergeConfigFiles();
die "Unsafe config file found: $unsafeConfig\n" if $unsafeConfig;
if (defined($Param{'superuser'})) {
$MYSQL_CNF{'mysql'}{'user'} = $Param{'superuser'};
$MYSQL_CNF{'mysqldump'}{'user'} = $Param{'superuser'};
}
if (defined($Param{'spassword'})) {
if ( $CMD && length($Param{'spassword'}) == 0 ) {
$Param{'spassword'} =
PromptPass("Password for MySQL superuser $Param{'superuser'}: ");
}
if ( length($Param{'spassword'}) > 0 ) {
$MYSQL_CNF{'mysql'}{'password'} = $Param{'spassword'};
$MYSQL_CNF{'mysqldump'}{'password'} = $Param{'spassword'};
}
}
WriteTempConfigFile();
#---------------------------------
# Inform user if he has not enough
# privileges to read the access-db
if ( $nerror=MySQLaccess::DB::OpenConnection() ) {
MySQLaccess::Report::Print_Error_Access($nerror);
exit 0;
}
# -----------------------
# Read MySQL ACL-files
if ($nerror=MySQLaccess::Grant::ReadTables()) {
MySQLaccess::Report::Print_Error_Access($nerror);
exit 0;
};
if ($Param{'edit'} and $nerror=MySQLaccess::Grant::ReadTables('tmp')) {
MySQLaccess::Report::Print_Error_Access($nerror);
exit 0;
}
#---------------------------------
# reload temporay grant-tables
# with data from original ones
if ( defined($Param{'copy'}) ) {
$nerror=MySQLaccess::DB::LoadTmpTables();
if ($nerror) {
MySQLaccess::Report::Print_Error_Access($nerror);
exit 0;
}
my $msg = "The grant-rules are copied from the grant-tables to\n"
. "the temporary tables.";
MySQLaccess::Report::Print_Message([$msg]);
# MySQLaccess::Report::Print_Footer();
# MySQLaccess::DB::CloseConnection();
# exit 0;
}
#---------------------------------
# preview result of changes in the
# grant-tables
if ( defined($Param{'preview'}) ) {
$aref=MySQLaccess::Grant::Diff_Privileges();
MySQLaccess::Report::Print_Diff_ACL($aref);
# MySQLaccess::Report::Print_Footer();
# MySQLaccess::DB::CloseConnection();
# exit 0;
}
#---------------------------------
# reload grant-tables
# with data from temporary tables
if ( defined($Param{'commit'}) ) {
if ($nerror = MySQLaccess::DB::CommitGrantTables()) {
MySQLaccess::Report::Print_Error_Access($nerror);
exit 0;
}
my $msg = "The grant-rules have been copied from the temporary tables\n"
. "to the grant-tables.";
my $msg1= "Don't forget to do an 'mysqladmin reload' before these\n"
. "changes take effect.";
my $msg2= "A backup-version of your original grant-rules are saved in the\n"
. "backup-tables, so you can always perform a 1-level rollback.";
MySQLaccess::Report::Print_Message([$msg,$msg1,$msg2]);
# MySQLaccess::Report::Print_Footer();
# MySQLaccess::DB::CloseConnection();
# exit 0;
}
#---------------------------------
# restore previous grant-rules
# with data from backup tables
if ( defined($Param{'rollback'}) ) {
if ($nerror = MySQLaccess::DB::RollbackGrantTables()) {
MySQLaccess::Report::Print_Error_Access($nerror);
exit 0;
}
my $msg = "The old grant-rules have been copied back from the backup tables\n"
. "to the grant-tables.";
my $msg1= "Don't forget to do an 'mysqladmin reload' before these\n"
. "changes take effect.";
MySQLaccess::Report::Print_Message([$msg,$msg1]);
# MySQLaccess::Report::Print_Footer();
# MySQLaccess::DB::CloseConnection();
# exit 0;
}
#----------------------------------
# show edit-taskbar
if ( defined($Param{'edit'})) {
if ($MySQLaccess::CGI ) {
MySQLaccess::Report::Print_Edit();
$print_usage=0;
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
}
else {
MySQLaccess::Report::Print_Edit();
$print_usage=0;
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
}
}
# -----------------------------
# Build list of users,dbs,hosts
# to process...
@all_dbs = @{MySQLaccess::DB::Get_All_dbs($Param{'db'})};
@all_users = @{MySQLaccess::DB::Get_All_users($Param{'user'})};
@all_hosts = @{MySQLaccess::DB::Get_All_hosts($Param{'host'})};
#if EDIT-mode
#@all_dbs_tmp = @{MySQLaccess::DB::Get_All_dbs($Param{'db'},'tmp')};
#@all_users_tmp = @{MySQLaccess::DB::Get_All_users($Param{'user'},'tmp')};
#@all_hosts_tmp = @{MySQLaccess::DB::Get_All_hosts($Param{'host'},'tmp')};
# -----------------------------
# Report access-rights for each
# tuple (host,user,db)
#$headers=0;
my %Access = ();
foreach $host (@all_hosts) {
foreach $user (@all_users) {
foreach $db (@all_dbs) {
MySQLaccess::Grant::Initialize();
%Access = MySQLaccess::Grant::Get_Access_Rights($host,$user,$db);
MySQLaccess::Report::Print_Access_rights($host,$user,$db,\%Access);
}
}
}
# -----------------------------
# End script
MySQLaccess::Report::Print_Footer();
MySQLaccess::DB::CloseConnection();
exit 0;
#############################################################
# FUNCTIONS #
###############
sub GetMode {
my $cmd=0;
my $cgi=0;
if (defined($ENV{'HTTP_HOST'})) { $cmd=0; $cgi=1; }
else { $cmd=1; $cgi=0; }
return ($cmd,$cgi);
}
# ================================
# sub PromptPass
# prompt tty for a password
# ================================
sub PromptPass {
my ($prompt) = @_;
my $password;
$ENV{PATH} = "/bin:/usr/bin";
$ENV{IFS} = " \t\n";
$ENV{SHELL} = "/bin/sh";
system "stty -echo";
print $prompt;
chomp($password = <STDIN>);
print "\n";
system "stty echo";
$password;
}
# =================================
# sub CheckUnsafeFile
# tell if a config file containing a password is unsafe
# =================================
sub CheckUnsafeFile {
my ($fname) = @_;
my ($dev, $ino, $mode, $nlink,
$uid, $gid, $rdev, $size,
$atime, $mtime, $ctime, $blksize, $blocks) = stat($fname);
if ( $uid != $< ) { # unsafe if owned by other than current user
return 1;
}
if ( $mode & 066 ) { # unsafe if accessible by other
return 1;
}
$fname =~ s#/[^/]+$##;
if ( (length $fname) > 0 ) {
return CheckUnsafeDir($fname);
}
return 0;
}
# =================================
# sub CheckUnsafeDir
# tell if a directory is unsafe
# =================================
sub CheckUnsafeDir {
my ($fname) = @_;
my ($dev, $ino, $mode, $nlink,
$uid, $gid, $rdev, $size,
$atime, $mtime, $ctime, $blksize, $blocks) = stat($fname);
# not owned by me or root
if ( ($uid != $<) && ($uid != 0) ) {
return 1;
}
if ( $mode & 022 ) { # unsafe if writable by other
return 1 unless $mode & 01000; # but sticky bit ok
}
$fname =~ s#/[^/]+$##;
if ( (length $fname) > 0 ) {
return CheckUnsafeDir($fname);
}
return 0;
}
# =================================
# sub MergeConfigFile
# merge data from .cnf file
# =================================
sub MergeConfigFile {
my ($fname) = @_;
my ($group, $item, $value);
if ( open CNF, $fname ) {
while (<CNF>) {
s/^\s+//;
next if /^[#;]/;
if ( /\[\s*(\w+)\s*]/ ) {
$group = $1;
$group =~ tr/A-Z/a-z/;
if ( !exists $MYSQL_CNF{$group} ) {
undef $group;
}
} elsif ( defined $group ) {
($item, $value) = /((?:\w|-)+)\s*=\s*(\S+)/;
# don't unquote backslashes as we just write it back out
if ( defined $item ) {
if ( $item =~ /^password$/ ) {
if ( CheckUnsafeFile($fname) ) {
$unsafeConfig = $fname;
}
}
if ( $group eq 'client' || $group eq "client-server") {
$MYSQL_CNF{'mysql'}{$item} = $value;
$MYSQL_CNF{'mysqldump'}{$item} = $value;
} else {
$MYSQL_CNF{$group}{$item} = $value;
}
}
}
}
close(CNF);
}
}
# =================================
# sub MergeConfigFiles
# merge options from config files
# NOTE: really should do two separate merges for each
# client to exactly duplicate order of resulting argument lists
# =================================
sub MergeConfigFiles {
my ($name,$pass,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwuid $<;
MergeConfigFile("@prefix@/my.cnf");
MergeConfigFile("@sysconfdir@/my.cnf");
MergeConfigFile("$dir/.my.cnf");
}
# =================================
# sub WriteTempConfigFile
# write
# =================================
sub WriteTempConfigFile {
sysopen CNFFILE, $MYSQL_CNF, O_RDWR|O_CREAT|O_EXCL, 0700
or die "sysopen $MYSQL_CNF: $!";
# groups may be in any order, generic groups such as [client] assumed
# here to be empty
foreach $group (keys %MYSQL_CNF) {
print CNFFILE "[$group]\n";
foreach $item (keys %{$MYSQL_CNF{$group}}) {
if ( defined $MYSQL_CNF{$group}{$item} ) {
print CNFFILE "$item=$MYSQL_CNF{$group}{$item}\n";
} else {
print CNFFILE "$item\n";
}
}
print CNFFILE "\n";
}
close(CNFFILE);
}
######################################################################
package MySQLaccess::DB;
###########
BEGIN {
$DEBUG = 2;
$DEBUG = $MySQLaccess::DEBUG unless ($DEBUG);
# Error-messages from the MySQL client
%ACCESS_ERR= ('Access_denied' => 'Access denied'
,'Dbaccess_denied' => 'Access to database denied'
,'Unrecognized_option' => 'unrecognized option'