xref: /illumos-gate/usr/src/lib/libbsm/auditxml.pm (revision c0c79a3f)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21#
22# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23# Use is subject to license terms.
24#
25# ident	"%Z%%M%	%I%	%E% SMI"
26#
27
28use xmlHandlers;
29
30package externalEvent;
31
321;
33
34sub new {
35    my $pkg = shift;
36    my $id  = shift;
37    my $obj = shift;
38
39    my @kid = $obj->getKids(); # kids of event are entry or allowed_types
40
41    # separate kids into classes and create hash of entries and an
42    # array of includes
43
44    my %entry = ();
45    my @entry = ();
46    my @allowed_types = ();
47    my @include = ();
48    my $internalName = '';
49
50    my $kid;
51    foreach $kid (@kid) {
52	my $class = $kid->getClass();
53	my $kidId = $kid->getAttr('id');
54
55	if ($class eq 'entry') {
56	    my $tokenId = 'undefined';
57	    my $format = '';
58	    my $internal = $kid->getKid('internal');
59	    if (defined $internal) {
60	      $tokenId = $internal->getAttr('token');
61	      $format = $internal->getAttr('format');
62	      $format = '' unless defined $format;
63	    }
64	    my $comment;
65	    my $commentKid = $kid->getKid('comment');
66	    if (defined $commentKid) {
67	    	$comment = $commentKid->getContent;
68	    }
69	    my $external = $kid->getKid('external');
70	    if (defined ($external)) {
71		$entry{$kidId} = [$external, $kid, $tokenId, $format, $comment];
72		push (@entry, $kidId);
73	    }
74	    else {
75		print STDERR "no external attributes defined for $id/$kidId\n";
76	    }
77	} # handle event id translation...
78	elsif ($class eq 'altname') {
79	    $internalName = $kid->getAttr('id');
80	    unless (defined $internalName) {
81		print STDERR "missing id for internal name of $id\n";
82		$internalName = 'error';
83	    }
84	}
85	elsif ($class eq 'allowed_types') {
86	    my $content = $kid->getContent();
87	    @allowed_types = (@allowed_types, split(/\s*,\s*/, $content));
88	}
89    }
90    my @entryCopy = @entry;
91    return bless {'id'			=> $id,
92		  'internalName'	=> $internalName,
93		  'allowed_types'	=> \@allowed_types,
94		  'entry'		=> \%entry,
95		  'entryList'		=> \@entry,
96		  'entryListCopy'	=> \@entryCopy,
97		  'include'		=> \@include,
98		  'xmlObj'		=> $obj}, $pkg;
99}
100
101# return id
102
103sub getExternalName {
104  my $pkg = shift;
105
106  return $pkg->{'id'};
107}
108
109
110# return internal name if it exists, else id
111
112sub getInternalName {
113    $pkg = shift;
114
115    if ($pkg->{'internalName'}) {
116	return $pkg->{'internalName'};
117    }
118    else {
119	return $pkg->{'id'};
120    }
121}
122
123# getNextEntry reads from 'entryList' destructively
124# but resets when the list after the list is emptied
125
126sub getNextEntry {
127    my $pkg = shift;
128
129    unless (@{$pkg->{'entryList'}}) {
130	@{$pkg->{'entryList'}} = @{$pkg->{'entryListCopy'}};
131	return undef;
132    }
133    my $id = shift @{$pkg->{'entryList'}};
134
135    return ($pkg->getEntry($id));  # getEntry returns an array
136}
137
138# getEntryIds returns list of all ids from entryList
139
140sub getEntryIds {
141    my $pkg = shift;
142    return (@{$pkg->{'entryList'}});
143}
144
145# getEntry returns a selected entry for the current event
146
147sub getEntry {
148    my $pkg = shift;
149    my $id  = shift;  #entry id
150
151    my $ref = $pkg->{'entry'};
152    my $array = $$ref{$id};
153
154    return @$array;
155}
156
157# getNextInclude reads from 'include' destructively
158
159sub getNextInclude {
160    my $pkg = shift;
161
162    return shift @{$pkg->{'include'}};
163}
164
165# getIncludes returns list of 'include'
166
167sub getIncludes {
168    my $pkg = shift;
169    return @{$pkg->{'include'}};
170}
171
172# return a reference to the list of event id's allowed for
173# this generic event
174
175sub getAllowedTypes {
176    my $pkg = shift;
177
178    return $pkg->{'allowed_types'};
179}
180
181package internalEvent;
182
1831;
184
185sub new {
186    my $pkg = shift;
187    my $id  = shift;
188    my $obj = shift;
189
190    my @kid = $obj->getKids(); # kids of event are entry
191
192    my @entry = ();
193
194    my $reorder = 0;
195    if ($reorder = $obj->getAttr('reorder')) {
196	$reorder = 1 if $reorder eq 'yes';
197    }
198    my $kid;
199    foreach $kid (@kid) {
200      my $class = $kid->getClass();
201      my $id = $kid->getAttr('id');
202
203      if ($class eq 'entry') {
204	my $internal = $kid->getKid('internal');
205	if (defined ($internal)) {
206	  push (@entry, [$internal, $kid]);
207	}
208	else {
209	  print STDERR "no internal attributes defined for $id\n";
210	}
211      }
212    }
213    return bless {'id'       => $id,
214		  'reorder'  => $reorder,
215		  'entry'    => \@entry,
216		  'xmlObj'   => $obj}, $pkg;
217}
218
219# getEntries returns a list of all entry references
220
221sub getEntries {
222    my $pkg = shift;
223
224    return undef unless @{$pkg->{'entry'}};
225
226    return @{$pkg->{'entry'}};
227}
228
229sub isReorder {
230  my $pkg = shift;
231
232  return $pkg->{'reorder'};
233}
234
235sub getId {
236    my $pkg = shift;
237
238    return $pkg->{'id'};
239}
240
241package eventDef;
242
243%uniqueId = ();
244
2451;
246
247sub new {
248    my $pkg = shift;
249    my $id  = shift;
250    my $obj = shift;
251    my $super = shift;
252
253    my $omit;
254    my $type;
255    my $header;
256    my $idNo;
257    my $javaToo;
258    my $title = '';
259    my @program = ();
260    my @see = ();
261
262    $omit = '' unless $omit = $obj->getAttr('omit');
263    $type = '' unless $type = $obj->getAttr('type');
264    $header = 0 unless $header = $obj->getAttr('header');
265    $idNo = '' unless $idNo = $obj->getAttr('idNo');
266
267    if ($idNo ne '' && $uniqueId{$idNo}) {
268        print STDERR "$uniqueId{$idNo} and $id have the same id ($idNo)\n";
269    }
270    else {
271        $uniqueId{$idNo} = $id;
272    }
273
274    return bless {'id'		=> $id,
275		  'header'	=> $header,
276		  'idNo'	=> $idNo,
277		  'omit'	=> $omit,
278		  'super'	=> $super,
279		  'type'	=> $type,
280		  'title'	=> $title,
281		  'program'	=> \@program,
282		  'see'		=> \@see,
283		  'external'	=> 0,
284		  'internal'	=> 0}, $pkg;
285}
286
287# putDef is called at the end of an <event></event> block, so
288# it sees a completed object.
289
290sub putDef {
291    my $pkg  = shift;
292    my $obj  = shift;  # ref to xmlHandlers event object
293    my $context = shift;
294
295    my $id = $pkg->{'id'};
296
297    if ($context eq 'internal') {
298	$pkg->{$context} = new internalEvent($id, $obj);
299	return undef;
300    } elsif ($context eq 'external') {
301	my $ref = $pkg->{$context} = new externalEvent($id, $obj);
302	return $ref->{'internalName'};
303    }
304}
305
306sub getId {
307    my $pkg = shift;
308
309    return $pkg->{'id'};
310}
311
312sub getHeader {
313    my $pkg = shift;
314
315    return $pkg->{'header'};
316}
317
318sub getIdNo {
319    my $pkg = shift;
320
321    return $pkg->{'idNo'};
322}
323
324sub getSuperClass {
325    my $pkg = shift;
326
327    return $pkg->{'super'};
328}
329
330sub getOmit {
331    my $pkg = shift;
332
333    return $pkg->{'omit'};
334}
335
336sub getType {
337    my $pkg = shift;
338
339    return $pkg->{'type'};
340}
341
342sub getTitle {
343    return shift->{'title'};
344}
345
346sub getProgram {
347    return shift->{'program'};
348}
349
350sub getSee {
351    return shift->{'see'};
352}
353
354sub getInternal {
355    my $pkg = shift;
356
357    return $pkg->{'internal'};
358}
359
360sub getExternal {
361    my $pkg = shift;
362
363    return $pkg->{'external'};
364}
365
366# this isn't fully implemented; just a skeleton
367
368package tokenDef;
369
3701;
371
372sub new {
373    my $pkg = shift;
374    my $obj = shift;
375    my $id  = shift;
376
377    $usage	= $obj->getAttr('usage');
378    $usage = '' unless defined $usage;
379
380    return bless {'id'		=> $id,
381		  'usage'	=> $usage
382		  }, $pkg;
383}
384
385sub getId {
386    my $pkg = shift;
387
388    return $pkg->{'id'};
389}
390
391sub getUsage {
392    my $pkg = shift;
393
394    return $pkg->{'usage'};
395}
396
397package messageList;
398
3991;
400
401sub new {
402    my $pkg = shift;
403    my $obj = shift;
404    my $id  = shift;
405    my $header = shift;
406    my $start = shift;
407    my $public = shift;
408    my $deprecated = shift;
409
410    my @msg = ();
411
412    my @kid = $obj->getKids(); # kids of msg_list are msg
413    my $kid;
414    foreach $kid (@kid) {
415	my $class = $kid->getClass();
416	if ($class eq 'msg') {
417	    my $text = $kid->getContent();
418	    $text = '' unless defined ($text);
419	    my $msgId = $kid->getAttr('id');
420	    if (defined ($msgId)) {
421	        push(@msg, join('::', $msgId, $text));
422	    }
423	    else {
424	        print STDERR "missing id for $class <msg>\n";
425	    }
426	}
427	else {
428	    print STDERR "invalid tag in <msg_list> block: $class\n";
429	}
430    }
431
432    return bless {'id'		=> $id,
433		  'header'	=> $header,
434		  'msg'		=> \@msg,
435		  'start'	=> $start,
436		  'public'	=> $public,
437		  'deprecated'	=> $deprecated
438		 }, $pkg;
439}
440
441sub getId {
442    my $pkg = shift;
443
444    return $pkg->{'id'};
445}
446
447sub getMsgStart {
448    my $pkg = shift;
449
450    return $pkg->{'start'};
451}
452
453sub getDeprecated {
454    my $pkg = shift;
455
456    return $pkg->{'deprecated'};
457}
458
459sub getMsgPublic {
460    my $pkg = shift;
461
462    return $pkg->{'public'};
463}
464
465sub getHeader {
466    my $pkg = shift;
467
468    return $pkg->{'header'};
469}
470
471# destructive read of @msg...
472
473sub getNextMsg {
474    my $pkg = shift;
475
476    my @msg = @{$pkg->{'msg'}};
477
478    return undef unless @msg;
479
480    my $text = pop(@msg);
481    $pkg->{'msg'} = \@msg;
482    return $text;
483}
484
485# returns all msgs
486sub getMsgs {
487    my $pkg = shift;
488
489    return @{$pkg->{'msg'}};
490}
491
492
493package auditxml;
494
495# These aren't internal state because the callback functions don't
496# have the object handle.
497
498@debug   = ();            # stack for nesting debug state
499%event   = ();            # event name => $objRef
500@event   = ();            # event id
501%token   = ();            # token name => $objRef
502@token   = ();            # token id
503%msg_list = ();           # messageList string list id to obj
504@msg_list = ();           # id list
505%service = ();            # valid service names
506%externalToInternal = (); # map external event name to internal event name
507
5081;
509
510sub new {
511    my $pkg  = shift;
512    my $file = shift;  # xml file to be parsed
513
514    register('event',      \&eventStart,  \&eventEnd);
515    register('entry',      0,             \&entry);
516    register('external',   0,             \&external);
517    register('internal',   0,             \&internal);
518    register('include',    0,             \&include);
519    register('token',      0,             \&token);
520    register('service',    0,             \&service);
521    register('msg_list',   0,             \&msg_list);
522    register('msg',        0,             \&msg);
523
524    # do not use register() for debug because register generates extra
525    # debug information
526
527    xmlHandlers::registerStartCallback('debug', \&debugStart);
528    xmlHandlers::registerEndCallback('debug', \&debugEnd);
529
530    $xml = new xmlHandlers(0, 'top level', $file);
531
532    return bless {'xmlObj'     => $xml,
533	          'firstToken' => 1,
534	          'firstEvent' => 1}, $pkg;
535}
536
537# local function -- register both the auditxml function and the
538# xmlHandler callback
539
540sub register {
541    my $localName     = shift;
542    my $startFunction = shift;
543    my $endFunction = shift;
544
545    if ($startFunction) {
546      xmlHandlers::registerStartCallback($localName, \&completed);
547	$startFunction{$localName} = $startFunction;
548    }
549    if ($endFunction) {
550      xmlHandlers::registerEndCallback($localName, \&completed);
551	$endFunction{$localName} = $endFunction;
552    }
553}
554
555sub completed {
556    my $obj = shift;
557    my $callbackSource = shift;
558
559    my $id  = $obj->getAttr('id');
560    my $class = $obj->getClass();
561
562    if ($main::debug) {
563	print "*** $callbackSource: $class", (defined ($id)) ? "= $id\n" : "\n";
564
565	my %attributes = $obj->getAttributes();
566	my $attribute;
567	foreach $attribute (keys %attributes) {
568	    print "*** $attribute = $attributes{$attribute}\n";
569	}
570	my $content = $obj->getContent();
571	print "*** content = $content\n" if defined $content;
572    }
573    if ($callbackSource eq 'start') {
574	&{$startFunction{$class}}($obj);
575    }
576    elsif ($callbackSource eq 'end') {
577	&{$endFunction{$class}}($obj);
578    }
579    else {
580	print STDERR "no auditxml function defined for $class\n";
581    }
582}
583
584# getNextEvent reads from @event destructively.  'firstEvent' could
585# be used to make a copy from which to read.
586
587sub getNextEvent {
588    my $pkg = shift;
589
590    return undef unless (@event);
591    if ($pkg->{'firstEvent'}) {
592	@token = sort @token;
593	$pkg->{'firstEvent'} = 1;
594    }
595
596    my $id = shift @event;
597
598    return $event{$id};
599}
600
601# returns all event ids
602sub getEventIds {
603   my $pkg = shift;
604
605   return @event;
606}
607
608# returns event for id
609sub getEvent {
610    my $pkg = shift;
611    my $id = shift;
612
613    return $event{$id};
614}
615
616sub getToken {
617    my $pkg = shift;
618    my $id = shift;
619
620    return $token{$id};
621}
622
623# getNextToken reads from @token destructively.  'firstToken' could
624# be used to make a copy from which to read.
625
626sub getNextToken {
627    my $pkg = shift;
628
629    return undef unless (@token);
630
631    if ($pkg->{'firstToken'}) {
632	@token = sort @token;
633	$pkg->{'firstToken'} = 1;
634    }
635    my $id = shift @token;
636
637    return $token{$id};
638}
639
640# return token Ids
641
642sub getTokenIds {
643    my $pkg = shift;
644
645    return @token;
646}
647
648# getNextMsgId reads from @msg_list destructively.
649
650sub getNextMsgId {
651    my $pkg = shift;
652
653    return undef unless (@msg_list);
654
655    my $id = shift @msg_list;
656
657    return ($id, $msg_list{$id});
658}
659
660sub getMsgIds {
661    my $pkg = shift;
662
663    return @msg_list;
664}
665
666sub getMsg {
667    my $pkg = shift;
668    my $id = shift;
669
670    return $msg_list{$id};
671}
672
673sub external {
674}
675
676sub internal {
677
678}
679
680sub eventStart {
681    my $obj  = shift;
682
683    my $id = $obj->getAttr('id');
684
685    unless ($id) {
686	print STDERR "eventStart can't get a valid id\n";
687	return;
688    }
689    unless (defined $event{$id}) {
690        my $super;
691	if ($super = $obj->getAttr('instance_of')) {
692	    $super = $event{$super};
693	} else {
694	    $super = 0;
695	}
696	$event{$id} = new eventDef($id, $obj, $super);
697        push (@event, $id);
698    } else {
699	print STDERR "duplicate event id: $id\n";
700    }
701}
702
703sub eventEnd {
704    my $obj  = shift;
705
706    my $id    = $obj->getAttr('id');
707    unless (defined $id) {
708	print STDERR "event element is missing required id attribute\n";
709	return;
710    }
711    print "event = $id\n" if $main::debug;
712
713    foreach my $kid ($obj->getKids) {
714    	my $class = $kid->getClass;
715    	next unless ($class =~ /title|program|see/);
716	my $content = $kid->getContent;
717	if ($class eq 'title') {
718	    $event{$id}->{$class} = $content;
719	} else {
720	    push @{$event{$id}->{$class}}, $content;
721	}
722    }
723    $event{$id}->putDef($obj, 'internal');
724
725    my $internalName = $event{$id}->putDef($obj, 'external');
726
727    $externalToInternal{$id} = $internalName if $internalName;
728}
729
730# class method
731
732#sub getInternalName {
733#    my $name = shift;
734#
735#    return $externalToInternal{$name};
736#}
737
738sub entry {
739}
740
741#sub include {
742#    my $obj  = shift;
743#
744#    my $id = $obj->getAttr('id');
745#
746#    if (defined $id) {
747#	print "include = $id\n" if $main::debug;
748#    }
749#    else {
750#	print STDERR "include element is missing required id attribute\n";
751#    }
752#}
753
754sub token {
755    my $obj  = shift;
756
757    my $id = $obj->getAttr('id');
758
759    if (defined $id) {
760	print "token = $id\n" if $main::debug;
761	$token{$id} = new tokenDef($obj, $id);
762	push (@token, $id);
763    }
764    else {
765	print STDERR "token element is missing required id attribute\n";
766    }
767}
768
769sub msg_list {
770    my $obj = shift;
771
772    my $id = $obj->getAttr('id');
773    my $header = $obj->getAttr('header');
774    my $start = $obj->getAttr('start');
775    my $public = $obj->getAttr('public');
776    my $deprecated = $obj->getAttr('deprecated');
777
778    $header = 0 unless $header;
779    $start = 0 unless $start;
780    $public = ($public) ? 1 : 0;
781    $deprecated = ($deprecated) ? 1 : 0;
782
783    if (defined $id) {
784	print "msg_list = $id\n" if $main::debug;
785	$msg_list{$id} = new messageList($obj, $id, $header, $start,
786	    $public, $deprecated);
787	push (@msg_list, $id);
788    }
789    else {
790	print STDERR
791	    "msg_list element is missing required id attribute\n";
792    }
793}
794
795sub msg {
796#    my $obj = shift;
797}
798
799# Service name was dropped during PSARC review
800
801sub service {
802    my $obj = shift;
803
804    my $name = $obj->getAttr('name');
805    my $id   = $obj->getAttr('id');
806
807    if ((defined $id) && (defined $name)) {
808	print "service $name = $id\n" if $main::debug;
809	$service{$name} = $id;
810    }
811    elsif (defined $name) {
812	print STDERR "service $name is missing an id number\n";
813    }
814    elsif (defined $id) {
815	print STDERR "service name missing for id = $id\n";
816    }
817    else {
818	print STDERR "missing both name and id for a service entry\n";
819    }
820}
821
822#sub getServices {
823#
824#    return %service;
825#}
826
827# <debug set="on"> or <debug set="off"> or <debug>
828# if the set attribute is omitted, debug state is toggled
829
830# debugStart / debugEnd are used to insure debug state is
831# scoped to the block between <debug> and </debug>
832
833sub debugStart {
834    my $obj = shift;
835
836    push (@debug, $main::debug);
837    my $debug = $main::debug;
838
839    my $state = $obj->getAttr('set');
840
841    if (defined $state) {
842	$main::debug = ($state eq 'on') ? 1 : 0;
843    }
844    else {
845	$main::debug = !$debug;
846    }
847    if ($debug != $main::debug) {
848	print 'debug is ', $main::debug ? 'on' : 'off', "\n";
849    }
850}
851
852sub debugEnd {
853    my $obj = shift;
854
855    my $debug = $main::debug;
856    $main::debug = pop (@debug);
857
858    if ($debug != $main::debug) {
859	print 'debug is ', $main::debug ? 'on' : 'off', "\n";
860    }
861}
862