-
Notifications
You must be signed in to change notification settings - Fork 1
/
kuang.pl.shar
executable file
·1482 lines (1482 loc) · 41.7 KB
/
kuang.pl.shar
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
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 01/09/1991 15:04 UTC by [email protected]
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 9782 -rw------- README.perl
# 1776 -rwx------ get-cf
# 6490 -rw------- kuang.1
# 16925 -rwx------ kuang.pl
# 284 -rwx------ kuang_all
# 1307 -rw------- put-cf
# 1274 -rw------- yagrip.pl
#
# ============= README.perl ==============
if test -f 'README.perl' -a X"$1" != X"-c"; then
echo 'x - skipping README.perl (File already exists)'
else
echo 'x - extracting README.perl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'README.perl' &&
XThis is a perl version of Dan's version of Bob Baldwin's Kuang program
X(originally written as some shell scripts and C programs).
X
XThe original intent was to improve the speed of kuang, which is
Xespecially important for installations like ours with several thousand
Xaccounts and NFS things and all that. The shell version of Kuang used
XC programs to add rules, get a groups members, determine the writers
Xof a file, and so on, which really slowed things down.
X
X "no" problems /etc staff writeable
X ------------- --------------------
Xshell kuang 2:14 (14) 12:26 (98) 0.1 p/s
Xperl kuang 1:10 (18) 2:34 (588) 3.8 p/s
X
XThe "no" problems column indicates the time taken (and number of plans
Xconsidered) for the shell and Perl versions of Kuang on a system with
Xno known security problems. The "/etc staff writeable" column gives
Xtiming and # of plans for a system with a /etc directory that is
Xwriteable by group staff, which contains several dozen users.
X
XAs you can see, the Perl version is a bit faster. Turns out there are
Xall sorts of details that need to be considered in real
Ximplementations of Kuang type programs, some of which are discussed
Xbelow.
X
X --- Steve Romig, CIS, Ohio State, October 1990
X
X------------------------------------------------------------------------------
X
XSome Features of the Perl Version
X
X Caches passwd/group file entries in an associative array for faster
X lookups. This is particularly helpful on insecure systems using YP
X where password and group lookups are slow and you have to do a lot of
X them...:-)
X
X Can specify target (uid or gid) on command line.
X
X Can use -l option to generate PAT for a goal.
X
X Can use -f to preload file owner, group and mode info, which is
X helpful in speeding things up and in avoiding file system
X 'shadows'... See the man page for details.
X
XFuture plans, things to fix:
X
X- An earlier version scanned the password file looking for generally
X accessible accounts (no password), which would be added to the
X uids.known list (in addition to -1, "other"). I had planned on also
X adding a password checker which would allow us to also add accounts
X with easily guessed passwords. Eventually I nuked the code that
X scanned the password file to speed things up, and further reflection
X reveals that it isn't wise to add the password scanning to Kuang
X itself (since there are many other things that might be considered
X in determining whether an account is accessible or not, and you
X probably don't want to add them all to Kuang).
X
X At some point we should add a command line option that allows us to
X add additional uid's (or gid's?) to the uids.known list. That way
X the user could run some other tool to scan the password file and
X generate a list of accessible accounts, which could then be fed to
X kuang. Makes it faster on clients using YP since most of the
X password file is the same for all N clients, why scan it N times.
X This would make it easier for the Kuang user to do smarter things
X to/with the password file checks (list all accounts with no password
X or easily guessed password, filter out "ok" entries (eg, sync) and
X etc.)
X
X- This version doesn't deal with uid's and gid's correctly. If there
X are several entries that list the same UID, but with different
X names, directories and shells, we'll only check plans for becoming
X one of them, rather than any of them, so some possible plans aren't
X even examined.
X
X Hmmm...this is easier than I thought - when we evaluate some plan
X for granting a particular uid, we need to evaluate plans for all
X usernames that can become that uid. Just stick a loop in there
X somewhere...get CF's for each of username's in turn.
X
X Bah, harder than I thought, since it'd have to scan the whole
X password file to figure which username/home directories can become
X which uid's. Similarly with groups.
X
X Current plan: by default, kuang will have to scan the whole password
X and group files so it can be sure to get all possible ways to become
X some uid or gid. Internally, really need several lists:
X
X mapping from uid to list of usernames that have that uid
X mapping from a username to home directory, shell
X mapping from gid to list of uids that have access to that
X gid when they login (either member of group with that gid or
X given as login group in passwd file)
X mapping from gid to list of group names for that gid
X
X Course, this means that we have to read the whole password and group
X file, most of which will be common to many machines (like in a YP
X environment). We could preload the tables above from files created
X once, containing the brunt of the YP info, and then augment that
X with the local passwd and group info on each host when kuang is
X invoked, but then we need to correctly interpret funky YP things
X like +@netgroup:::*:..., which means that the uid has a name but no
X password here...and similarly with shell substitutions and so on.
X Bah.
X
X- In a large environment (like ours, 260+ machines, 30+ file systems
X on as many servers, 2000 password file entries served by YP) it
X would be nice to 'precompute' successful plans that would be common
X to all systems. In particular, plans for becoming most of the users
X with home directories on the NFS file systems would be useful, since
X we don't really want to recheck these on each host. You wouldn't
X want the plan to be too deep - probably shouldn't span more than 2
X uids (1 on each end: grant u.romig grant g.staff write ~foo/.login
X grant u.foo). I'm thinking that you could feed a list of these
X precomputed plans to kuang and add some code that causes it to
X splice in relevant plans where it can to short cut the planning
X steps. For example, if one of the plans in uids.next is something
X like "grant u.foo ...", and I have the precomputed plan mentioned
X above, I could splice the two: "grant u.romig grant g.staff write
X ~foo/.login grant u.foo ..." and skip all the normal steps that
X would've been taken to get there.
X
X I'm not sure this is even feasible or useful. Food for thought.
X
X- Hmmm...thinking about it, it seems like some of the steps are a bit
X too implicit...maybe the rules should be broken out a bit more.
X That will cost in processing time, though.
X
X- Would be really, really nice to be able to deal with PATH variables
X - location of ., who can write elements of path, etc. Basic rule is
X "anyone who can replace anything in any of path directories or the
X path directories themselves can become that PATH's user..." This
X can be really messy though - in our environment, the path for a user
X will depend on the architecture type of the machine that he is
X logged into, and to get the path, you'd have to read and interpret
X his .login (including variable assignments, source's and
X conditionals). Urf. One wonders whether it might be better to have
X something running as root that su's to each username in turn and
X gets the path that way...:-)
X
X- The kuang described in Baldwin's dissertation is somewhat different
X in nature from this one. The original computes a Privilege Access
X Table (PAT) which describes for each uid and gid which uids have
X access to that uid. To assess security, we compare this against the
X security policy for the site, which similarly describes which uid's
X are supposed to have access to each uid and gid. A sample SP might
X be that each uid should be accessible only by itself and root, and
X each gid should be accessible only to the members of that group and
X root. If the PAT listed additional uid's for some priv, that would
X constitute a violation of the Security Policy for the site.
X
X The current kuang is different. It registers Success (a problem was
X found) if it determines that some uid in the uids.known list (-1,
X "other" by default) can access the target privilege. It may find
X along the way that extra uids can access some uid, but these aren't
X reported as specific problems unless they are added to the
X uids.known list.
X
X We could do something similar to the kuang described in the paper by
X setting uids.known to be all the uids that aren't in the security
X policy table for the target uid, and running kuang against the
X target. This would report success for each uid that could access
X the target. You could do similar things with groups - uids.known
X would be all the uids that aren't members of the group...
X
X Alternately, we could simply have kuang record the list of uids that
X can access the target priv and print the list when its done. That
X way you could iterate kuang against all uids and gids and compare
X the resulting PAT against your security policy and record the
X differences. You'd probably want to record the plan for each uid
X reported also.
X
X On our system this would mean running kuang roughly 2500
X times to check 1 host, and we have about 300 hosts...urf...assuming
X that each kuang invocation has to check 50 plans, that's a total of
X 125,000 plans per host, or about an hour of real time...not as bad
X as it could be, though.
X
X- It would be nice to add to the list of rules. It would be especially
X nice to extract the rules from the code so that we can create site
X specific rule files (for example, we use X11r4 here, and many users
X have a .Xinitrc that contains shell commands that get executed when
X they login.)
X
X Easiest way to do this would be to extract the rules as Perl code so
X we can take advantage of conditionals and so on, and include them
X within the body of kuang somehow. A sample rule in perl:
X
X if (&shell($uid) eq "/bin/csh") {
X &addto("files", &home($uid)."/.login",
X "replace .login $plan");
X }
X
X which simply means "if the user's shell is csh, then try to replace
X his .login file."
X
SHAR_EOF
chmod 0600 README.perl ||
echo 'restore of README.perl failed'
Wc_c="`wc -c < 'README.perl'`"
test 9782 -eq "$Wc_c" ||
echo 'README.perl: original size 9782, current size' "$Wc_c"
fi
# ============= get-cf ==============
if test -f 'get-cf' -a X"$1" != X"-c"; then
echo 'x - skipping get-cf (File already exists)'
else
echo 'x - extracting get-cf (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'get-cf' &&
X#! /usr/local/bin/perl
X
X@dot_files = (
X ".login", ".logout", ".cshrc", # csh, cshe or tcsh
X ".profile", # ksh, sh
X ".env", # ksh
X ".alias", ".aliases", # common for all shells
X "user.ps", ".user.ps", "tools.ps", ".tools.ps",
X "startup.ps", ".startup.ps", # NeWS
X ".mgrc", # MGR
X ".X11init", ".awmrc", ".twmrc", ".xinitrc", # X11
X ".emacs" # emacs
X);
X
X%seen = {};
X
Xopen(HOST, "/bin/hostname |") || die "can't get the hostname";
Xchop($hostname=<HOST>);
Xclose(HOST);
X
Xuser_loop:
X for (($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent();
X $name ne "";
X ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent()) {
X
X #
X # If the user has a home directory on this server, get the info
X # about the directory, his CF's and so on.
X #
X if ($dir =~ m,^/n/$hostname/,) {
X if (! -d $dir) {
X printf(stderr "home directory '%s' for user '%s' doesn't exist.\n",
X $dir,
X $name);
X next user_loop;
X }
X
X ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
X $atime,$mtime,$ctime,$blksize,$blocks)
X = stat(_);
X $mode = $mode & 07777;
X
X &spit_it_out("d", $uid, $gid, $mode, $dir);
X
X foreach $file (@dot_files) {
X $path = "$dir/$file";
X
X if (-f $path) {
X ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
X $atime,$mtime,$ctime,$blksize,$blocks)
X = stat(_);
X $mode = $mode & 07777;
X
X &spit_it_out("f", $uid, $gid, $mode, $dir);
X }
X }
X }
X }
X
X
X
X
Xsub spit_it_out {
X local($type, $uid, $gid, $mode, $name) = @_;
X
X if (defined($seen{$name})) {
X return;
X }
X
X printf("%s %d %d 0%o %s\n", $type, $uid, $gid, $mode, $name);
X $seen{$name} = 1;
X}
X
SHAR_EOF
chmod 0700 get-cf ||
echo 'restore of get-cf failed'
Wc_c="`wc -c < 'get-cf'`"
test 1776 -eq "$Wc_c" ||
echo 'get-cf: original size 1776, current size' "$Wc_c"
fi
# ============= kuang.1 ==============
if test -f 'kuang.1' -a X"$1" != X"-c"; then
echo 'x - skipping kuang.1 (File already exists)'
else
echo 'x - extracting kuang.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'kuang.1' &&
X.TH KUANG 1 "4 October 1990"
X.SH NAME
Xkuang \- find security problems through rule based analysis
X.SH SYNOPSIS
X.B kuang
X.RB "[\|" \-v "\|]"
X.RB "[\|" \-d "\|]"
X.RB "[\|" \-l "\|]"
X.RB "[\|" \-D "\|]"
X.RB "[\|" \-f filedata "\|]"
X.RB "[\|"
X.IR u.username "\|]"
X.br
X.B kuang
X.RB "[\|" \-v "\|]"
X.RB "[\|" \-d "\|]"
X.RB "[\|" \-l "\|]"
X.RB "[\|" \-D "\|]"
X.RB "[\|" \-f filedata "\|]"
X.RB "[\|"
X.IR g.groupname "\|]"
X.br
X.SH DESCRIPTION
X.LP
X.B kuang
Xuses rule based analysis to examine the current security configuration
Xof a site and determine whether certain security problems exist.
X
X.B kuang
Xcontains embedded rules that describe the projection model and
Xsome of the attacker tricks used on Unix systems. It uses these rules
Xto reason backward from a desired goal (such as "grant u.root"),
Xgenerating potential "attack" plans from the rules and file system
Xstate and then evaluating them to see whether they are reachable
Xaccording to the state recorded in the password and group files and in
Xthe ownership and modes of the file systems.
X
XBy default,
X.B kuang
Xuses "grant u.root" as its initial goal. You can change that by
Xspecifying a username (u.username) or groupname (g.groupname) on the
Xcommand line. Normally
X.B kuang
Xdetermines a plan to be successful if it determines that anyone
X(u.other) can become the initial goal.
X
XThe
X.B \-v
Xoption causes
X.B kuang
Xto print a message about every plan added to the evaluation list.
XThis can help one to understand how
X.B kuang
Xworks. The
X.B \-d
Xoption causes
X.B kuang
Xto print a message when it evaluates a plan to determine whether to
Xretain it and add onto it or ignore it. These options will often
Xproduce lots of output, beware.
X
XNormally
X.B kuang
Xonly registers success when it finds that everyone on the system can
Xbecome the target uid or gid. With the
X.B \-l
Xoption,
X.B kuang
Xwill list every uid that can become the goal. This provides a more
Xcomplete picture of the state of security - you might deem it a
Xproblem if several users can become root, even if the rest cannot.
X
XOne might adopt the view that each uid should only be accessible by
Xitself and root, and that each gid should be accessible only by the
Xmembers of that group and root. One can then compare the expected
Xaccess list for a given uid or gid against the
X.B kuang
Xgenerated list to find security problems that
X.B kuang
Xwouldn't ordinarily tell you about.
X
XThe goals that
X.B kuang
Xuse seem cryptic, but are really pretty straightforward. Each goal
Xconsists of a list of <action> <object> pairs. Typical actions are
Xgrant, write and replace. Typical objects are user names
X(u.username), group names (g.groupname) and files names. The goal
X"grant u.root" means to have access to the root UID (0), in other
Xwords, to be able to run any program using that uid. Similarly,
X"grant g.staff" means to have access to group staff. The long goal
X"grant u.bill grant g.graphics replace /n/shoe/0/fred replace
X/n/shoe/0/fred/.profile grant u.fred grant g.staff" means become
Xuser bill, get access to the graphics group, replace the file
X/n/shoe/0/fred, replace /n/shoe/0/fred/.profile, become fred,
Xgrant access to the staff group. The problem that allows this to
Xhappen is that the /n/shoe/0 directory is writeable by the graphics
Xgroup, meaning that anyone in that group can replace the .profile file
Xfor the fred user and gain access to that account and the groups it
Xbelongs to when fred next logs in. Ooops.
X
XTo do a thorough job,
X.B kuang
Xreally needs to be able to access all of
Xthe controlling files of all users. In some environments, home
Xdirectories are located in NFS mounted file systems where the client
Xdoesn't have root access.
X
XProblem is that some home directories may be
Xprotected so that group foo can read/write them, but OTHER can't.
X.B kuang
Xrunning as some user not in group foo won't be able to read or
Xsearch the directory, creating a blind spot that may hide security
Xproblems (for example, if group foo can write that user's .login and
Xgain access to some other important priv...) Running
X.B kuang
Xas root
Xwon't help unless we are running on the server that exports that
Xfile system, since root==nobody through NFS here. Of course, then
Xyou'll find other blind spots on other servers, meaning that you'll
Xnever be able to see a complete picture of how things are from any
Xspot on the net. Running
X.B kuang
Xon every machine might not even
Xhelp, since the blind spots might prevent them from seeing viable
Xpaths to Success on any of the machines. Sigh.
X
XSoooo we've added a
X.B -f
Xoption that causes
X.B kuang
Xto preload owner, group and mode information for a list of files.
XEach line of the file should be of the form "type uid gid mode name".
X.B type
Xis ignored by
X.B kuang.
X.B uid
Xand
X.B gid
Xare the user and group ID numbers, in decimal.
X.B mode
Xis the permissions for the file, in octal. And
X.B name
Xis the name of the file. We've also added a program called
X.B get-cf
Xthat can be run as root on a server to create a file of the above form
Xfor the control files for the user's with home directories on that
Xserver. Then you can run
X.B get-cf
Xon every server as root, concatenate all the data together, and
Xpreload it into Perl. This will fix the shadow problems mentioned
Xabove and should also speed things up since you won't need to do all
Xthe file system references.
X
X.B kuang -f file
Xwill use a DBM database in place of a text file if file.dir exists.
XTo create a DBM database from a text file of the form described above,
Xuse
X.B kuang -f file -D.
XThis will suck in the text file and create a DBM database from it and
Xquit. This speeds up kuang's initialization somewhat, though it isn't
Xclear that its worth doing unless you have a local disk for the DBM
Xfile.
X
X.SH "SEE ALSO"
X"Rule Based Analysis of Computer Security", Robert W. Baldwin, MIT, June 1987.
X.SH NOTES
X.LP
XThis version of
X.B kuang
Xis based on the shell script versions that Dan Farmer included with
Xthe
X.B COPS
Xsecurity package, which in turn were based on code written by Robert
XBaldwin himself.
X
XYou should read the other documentation that should come with this
Xversion and modify the rules in
X.B kuang
Xto suite your site.
X
X.SH BUGS
X.LP
XThe rules should be extracted from the code so that they could be
Xaugmented in a site specific fashion more readily.
X
XThe system doesn't work correctly when multiple users in the password
Xfile share the same UID. In that event, it only checks plans for the
Xfirst.
SHAR_EOF
chmod 0600 kuang.1 ||
echo 'restore of kuang.1 failed'
Wc_c="`wc -c < 'kuang.1'`"
test 6490 -eq "$Wc_c" ||
echo 'kuang.1: original size 6490, current size' "$Wc_c"
fi
# ============= kuang.pl ==============
if test -f 'kuang.pl' -a X"$1" != X"-c"; then
echo 'x - skipping kuang.pl (File already exists)'
else
echo 'x - extracting kuang.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'kuang.pl' &&
X#! /usr/local/bin/perl
X
X#
X# kuang - rule based analysis of Unix security
X#
X# Perl version by Steve Romig of the CIS department, The Ohio State
X# University, October 1990.
X#
X# Based on the shell script version by Dan Farmer from his COPS
X# package, which in turn is based on a shell version by Robert
X# Baldwin.
X#
X
Xdo 'yagrip.pl' ||
X die "can't do yagrip.pl";
X
X$options = "vdlf:D";
X$usage = "usage: kuang [-v] [-d] [-l] [-D] [-f filedata] [u.username|g.groupname]\n";
X
X#
X# Simple Unix Kuang, a security checking program.
X#
X# This is a perl version of Dan Farmer's version of Bob Baldwin's
X# shell scripts.
X#
X
X#
X# passwd_byuid lookup a password entry by uid, return as
X# (name, password, directory, shell) list.
X#
Xsub passwd_byuid {
X local($uid) = @_;
X local($name, $passwd, $gid, $quota, $comment, $gcos, $dir, $shell);
X
X if (! defined($passwd_byuid_list{$uid})) {
X ($name, $passwd, $t_uid, $gid, $quota, $comment, $gcos, $dir, $shell) =
X getpwuid($uid);
X
X if ($t_uid eq "") {
X return();
X }
X
X $passwd_byuid_list{$uid} = join(':', $name, $passwd, $dir, $shell);
X $passwd_byname_list{$name} = $uid;
X }
X
X return(split(/:/, $passwd_byuid_list{$uid}));
X}
X
Xsub passwd_byname {
X local($name) = @_;
X local($passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell);
X
X if (! defined($passwd_byname_list{$name})) {
X ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) =
X getpwnam($name);
X
X if ($uid eq "") {
X return();
X }
X
X $passwd_byuid_list{$uid} = join(':', $name, $passwd, $dir, $shell);
X $passwd_byname_list{$name} = $uid;
X }
X
X return($passwd_byname_list{$name});
X}
X
X#
X# group_bygid lookup a group entry by gid, return as
X# (name, members) list.
X#
Xsub group_bygid {
X local($gid) = @_;
X local($name, $t_passwd, $t_gid, $members);
X
X if (! defined($group_bygid_list{$gid})) {
X ($name, $t_passwd, $t_gid, $members) = getgrgid($gid);
X
X if ($t_gid eq "") {
X return();
X }
X
X $group_bygid_list{$gid} = join(':', $name, $members);
X $group_byname_list{$name} = $gid;
X }
X
X return(split(/:/, $group_bygid_list{$gid}));
X}
X
Xsub group_byname {
X local($name) = @_;
X local($gname, $passwd, $gid, $members);
X
X if (! defined($group_byname_list{$name})) {
X ($gname, $passwd, $gid, $members) = getgrnam($name);
X
X if ($gid eq "") {
X printf(stderr "A group named '$name' does not exist!\n");
X exit(1);
X }
X
X $group_bygid_list{$gid} = join(':', $name, $members);
X $group_byname_list{$name} = $gid;
X }
X
X return($group_byname_list{$name});
X}
X
X#
X# Do various initialization type things.
X#
X
Xsub init_kuang {
X local($which, $name, $uid, $gid);
X local($f_type, $f_uid, $f_gid, $f_mode, $f_name);
X local($count);
X
X $which = "u";
X $name = "root";
X
X #
X # Deal with args...
X #
X
X &getopt($options) ||
X die $usage;
X
X if ($#ARGV == 0) {
X ($which, $name) = split(/\./, $ARGV[0]);
X
X if ($name eq "") {
X $name = $which;
X $which = "u";
X }
X
X if ($which ne "u" && $which ne "g") {
X printf(stderr "target must be given as u.user or g.group\n");
X exit(1);
X }
X } elsif ($#ARGV > 0) {
X printf(stderr $usage);
X exit(1);
X }
X
X #
X # Preload the file data...
X #
X if (defined($opt_f)) {
X #
X # If we are dumping the file data to a DBM file, nuke the existing
X # ones and open the dbm file. Otherwise, open the DBM file
X # only if they exist.
X #
X $read_from_file = 1;
X
X if (defined($opt_D)) {
X unlink("$opt_f.dir", "$opt_f.pag");
X
X dbmopen(files, $opt_f, 0644) ||
X die sprintf("can't open DBM file '%s'", $opt_f);
X } elsif (-f "$opt_f.dir") {
X dbmopen(files, $opt_f, 0644) ||
X die sprintf("can't open DBM file '%s'", $opt_f);
X
X $read_from_file = 0;
X }
X
X if ($read_from_file) {
X open(FILEDATA, $opt_f) ||
X die sprintf("kuang: can't open '%s'", $opt_f);
X
X $count = 0;
X while (<FILEDATA>) {
X $count++;
X
X chop;
X ($f_type, $f_uid, $f_gid, $f_mode, $f_name) = split;
X
X if ($count % 1000 == 0) {
X printf("line $count, reading entry for $f_name\n");
X }
X $files{$f_name} = join(' ', $f_uid, $f_gid, $f_mode);
X }
X
X close(FILEDATA);
X }
X }
X
X if (defined($opt_D)) {
X dbmclose(files);
X
X exit(0);
X }
Xif (defined($opt_v)) {
X printf("done with files\n");
X }
X #
X # Need some of the password and group stuff. Suck in passwd and
X # group info, store by uid and gid in an associative array of strings
X # which consist of fields corresponding to the passwd and group file
X # entries (and what the heck, we'll use : as a delimiter also...:-)
X #
X
X $passwd_byuid_list{-1} = "OTHER:::"; # add an entry for OTHER
X $passwd_byname_list{"OTHER"} = -1;
X
X $uids_known{-1} = ""; # we can access OTHER
X %uids_new = ();
X
X %gids_known = ();
X %gids_new = ();
X
X %files_new = ();
X
X #
X # Set up initial goal: become target user or group
X #
X if ($which eq "u") {
X $uid = &passwd_byname($name);
X if ($uid ne "") {
X &addto("uids", $uid, "grant u.$name do anything");
X } else {
X printf(stderr "There is no user with username '$name'.\n");
X exit(1);
X }
X } else {
X $gid = &group_byname($name);
X if ($gid ne "") {
X &addto("gids", $gid, "grant g.$name");
X } else {
X printf(stderr "There is no group named '$name'.\n");
X exit(1);
X }
X }
X}
X
X#
X# Get the home directory for this UID from the passwd file cache.
X#
Xsub gethome {
X local($uid) = @_;
X local($tmp, $home);
X
X ($tmp, $tmp, $home, $tmp) = &passwd_byuid($uid);
X return($home);
X}
X
X#
X# Get the writers of the named file - return as (UID, GID, OTHER)
X# triplet. Owner can always write, since he can chmod the file if he
X# wants.
X#
X# (fixme) are there any problems in this sort of builtin rule? should
X# we make this knowledge more explicit?
X#
Xsub filewriters {
X local($name) = @_;
X local($tmp, $mode, $uid, $gid, $other);
X
X #
X # Check the file cache - avoid disk lookups for performance and
X # to avoid shadows...
X #
X if (defined($files{$name})) {
X $cache_hit++;
X
X ($uid, $gid, $mode) = split(/ /, $files{$name});
X $mode = oct($mode);
X } else {
X $cache_miss++;
X
X if (! -e $name && $read_from_file) {
X $files{$name} = "";
X return;
X }
X
X ($tmp,$tmp,$mode,$tmp,$uid,$gid) = stat(_);
X if ($read_from_file) {
X $files{$name} = join(' ', $uid, $gid, $mode);
X }
X }
X
X if (($mode & 020) != 020) {
X $gid = "";
X }
X
X if (($mode & 02) == 02) {
X $other = 1;
X } else {
X $other = 0;
X }
X
X return($uid, $gid, $other);
X}
X
X#
X# return # of entries in given associative array.
X#
Xsub sizeof {
X local(*which) = @_;
X local(@keywords);
X
X @keywords = keys %which;
X return($#keywords + 1);
X}
X
X#
X# return appropriate entry from named associative array of given type.
X# returns a (key, value) pair - if key is "", there was no entry.
X#
Xsub getentry {
X local($which, $type, $key) = @_;
X local($newkey, $value);
X
X $newkey = "";
X $value = "";
X
X which: {
X if ($which eq "uids") {
X type0: {
X if ($type eq "known") {
X if (defined($uids_known{$key})) {
X $newkey = $key; $value = $uids_known{$key};
X }
X last type0;
X }
X
X if ($type eq "new") {
X if (defined($uids_new{$key})) {
X $newkey = $key; $value = $uids_new{$key};
X }
X last type0;
X }
X
X if ($type eq "pending") {
X if (defined($uids_pending{$key})) {
X $newkey = $key; $value = $uids_pending{$key};
X }
X last type0;
X }
X
X if ($type eq "old") {
X if (defined($uids_old{$key})) {
X $newkey = $key; $value = $uids_old{$key};
X }
X last type0;
X }
X
X printf(stderr "kuang: fatal error in getentry: type is wrong (%s)\n",
X $type);
X exit(1);
X }
X
X last which;
X }
X
X if ($which eq "gids") {
X type1: {
X if ($type eq "known") {
X if (defined($gids_known{$key})) {
X $newkey = $key; $value = $gids_known{$key};
X }
X last type1;
X }
X
X if ($type eq "new") {
X if (defined($gids_new{$key})) {
X $newkey = $key; $value = $gids_new{$key};
X }
X last type1;
X }
X
X if ($type eq "pending") {
X if (defined($gids_pending{$key})) {
X $newkey = $key; $value = $gids_pending{$key};
X }
X last type1;
X }
X
X if ($type eq "old") {
X if (defined($gids_old{$key})) {
X $newkey = $key; $value = $gids_old{$key};
X }
X last type1;
X }
X
X printf(stderr "kuang: fatal error in getentry: type is wrong (%s)\n",
X $type);
X exit(1);
X }
X
X last which;
X }
X
X if ($which eq "files") {
X type2: {
X if ($type eq "known") {
X if (defined($files_known{$key})) {
X $newkey = $key; $value = $files_known{$key};
X }
X last type2;
X }
X
X if ($type eq "new") {
X if (defined($files_new{$key})) {
X $newkey = $key; $value = $files_new{$key};
X }
X last type2;
X }
X
X if ($type eq "pending") {
X if (defined($files_pending{$key})) {
X $newkey = $key; $value = $files_pending{$key};
X }
X last type2;
X }
X
X if ($type eq "old") {
X if (defined($files_old{$key})) {
X $newkey = $key; $value = $files_old{$key};
X }
X last type2;
X }
X
X printf(stderr "kuang: fatal error in getentry: type is wrong (%s)\n",
X $type);
X exit(1);
X }
X
X last which;
X }
X
X printf(stderr "kuang: fatal error in getentry: which is wrong (%s)\n",
X $which);
X exit(1);
X }
X
X return($newkey, $value);
X}
X
X
X#
X# stores a (key, value) in the associative array of the given type.
X#
Xsub putentry {
X local($which, $type, $key, $value) = @_;
X
X which: {
X if ($which eq "uids") {
X type0: {
X if ($type eq "known") {
X $uids_known{$key} = $value; last type0;
X }
X
X if ($type eq "new") {
X $uids_new{$key} = $value; last type0;
X }
X
X if ($type eq "pending") {
X $uids_pending{$key} = $value; last type0;
X }
X
X if ($type eq "old") {
X $uids_old{$key} = $value; last type0;
X }
X
X printf(stderr "kuang: fatal error in putentry: type is wrong (%s)\n",
X $type);
X exit(1);
X }
X
X last which;
X }
X
X if ($which eq "gids") {
X type1: {
X if ($type eq "known") {
X $gids_known{$key} = $value; last type1;