8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / perl / contrib / Sun / Solaris / BSM / _BSMparse.pm
blobacd43cbd2590026bbb53385f255431ef3a92f628
2 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 # Use is subject to license terms.
5 # CDDL HEADER START
7 # The contents of this file are subject to the terms of the
8 # Common Development and Distribution License (the "License").
9 # You may not use this file except in compliance with the License.
11 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
12 # or http://www.opensolaris.org/os/licensing.
13 # See the License for the specific language governing permissions
14 # and limitations under the License.
16 # When distributing Covered Code, include this CDDL HEADER in each
17 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
18 # If applicable, add the following below this CDDL HEADER, with the
19 # fields enclosed by brackets "[]" replaced with your own identifying
20 # information: Portions Copyright [yyyy] [name of copyright owner]
22 # CDDL HEADER END
25 # WARNING -- this package implements a Sun private interface; it may
26 # change without notice.
28 package Sun::Solaris::BSM::_BSMparse;
29 require 5.005;
30 use strict;
31 use Exporter;
32 use Sun::Solaris::Utils qw(gettext);
34 use vars qw($VERSION $failedOpen
35 %EXPORT_TAGS @ISA @EXPORT_OK @EXPORT_FAIL);
37 $VERSION = '1.01';
39 @ISA = qw(Exporter);
40 my @constants = qw();
41 @EXPORT_OK = qw(readAttr readEvent readClass filterLabel filterCallName
42 readControl getPathList readUser ckAttrEvent);
43 @EXPORT_FAIL = qw($failedOpen);
44 %EXPORT_TAGS = (ALL => \@EXPORT_OK);
46 $failedOpen = gettext("failed to open %s: %s");
48 sub new {
49 my $obj = shift;
50 my $debug = shift; # bool
51 my $filters = shift; # options for filtering
53 my $dir = '/etc/security';
54 my $attrDir = '/usr/lib/audit';
55 my $configDir = $dir;
56 $attrDir = shift if (@_); # override for test
57 $configDir = shift if (@_); # ditto
59 my $suffix = '';
60 $suffix = shift if (@_); # test, again
62 $obj = ref($obj) || $obj;
64 my ($recordf, $classf, $controlf, $eventf, $userf) =
65 ("$attrDir/audit_record_attr$suffix",
66 "$configDir/audit_class$suffix",
67 "$configDir/audit_control$suffix",
68 "$configDir/audit_event$suffix",
69 "$configDir/audit_user$suffix");
71 return (bless {
72 'attrFile' => $recordf,
73 'classFile' => $classf,
74 'classFilter' => $filters->{'classFilter'},
75 'controlFile' => $controlf,
76 'debug' => $debug,
77 'eventFile' => $eventf,
78 'eventFilter' => $filters->{'eventFilter'},
79 'idFilter' => $filters->{'idFilter'},
80 'havePath' => 0,
81 'kernelDefault' => '',
82 'userDefault' => '',
83 'userFile' => $userf}, $obj);
86 # readAttr
87 # read the hand edited attrFile file
89 # return a hash reference
91 sub readAttr {
92 my $obj = shift;
94 my $file = $obj->{'attrFile'};
95 my $fileHandle = do {local *FileHandle; *FileHandle};
96 open($fileHandle, $file) or die sprintf("$failedOpen\n", $file, $!);
98 my $count = 0;
99 my $lastAttr = '';
100 my $lastMacro = '';
102 my $attrState = -1;
103 my $caseState = 0;
104 my $label;
105 my $callName = '';
106 my $skip = '';
107 my $description = 'none';
108 my $format = 'none';
109 my $comment = '';
110 my $title = 'none';
111 my $note = '';
112 my $case = '';
113 my @case = ();
114 my %skipClass;
115 my %attr = ();
116 my %token = ();
117 my $classFilter = $obj->{'classFilter'};
118 $classFilter = '' unless (defined ($classFilter));
120 my %noteAlias = ();
121 while (<$fileHandle>) {
122 chomp;
123 s/#.*//; # remove comment
124 next if (/^\s*$/);
126 if ($attrState < 0) { # initial state: header info
127 # continue assigning lines to multiline macros
128 # type: message
129 if ( $lastMacro ne '' ) {
130 my ($mcr, $attr) = split(/\s*:\s*/, $lastMacro);
132 if ($mcr eq "message") {
133 chomp($noteAlias{$attr});
134 chop($noteAlias{$attr});
136 $_ =~ /^\s*(.*)/i;
137 $noteAlias{$attr} .= $1;
139 $lastMacro = chkBslash($lastMacro, \$1);
141 next;
144 $lastMacro = '';
145 if (/^\s*skipClass\s*=\s*(.*)/i) {
146 my $class = $1;
147 # don't skip what you're searching for
148 next if (index(lc($classFilter),lc($class)) > -1);
149 $skipClass{$1} = 1;
150 next;
152 elsif (/^\s*token\s*=\s*(.*)/i) {
153 my ($attr, $value) = split(/\s*:\s*/, $1);
154 $token{$attr} = $value;
155 next;
157 elsif (/^\s*message\s*=\s*(.*)/i) {
158 my ($attr, $value) = split(/\s*:\s*/, $1);
159 $noteAlias{$attr} = $value;
160 $lastMacro = chkBslash("message:$attr", \$1);
161 next;
163 elsif (/^\s*kernel\s*=\s*(.*)/i) {
164 my ($attr, $value) = split(/\s*:\s*/, $1);
165 $obj->{'kernelDefault'} = $1;
166 next;
168 elsif (/^\s*user\s*=\s*(.*)/i) {
169 my ($attr, $value) = split(/\s*:\s*/, $1);
170 $obj->{'userDefault'} = $1;
171 next;
175 # continue assigning lines to multiline attributes
176 # type: case, comment, note, format
177 if ( $lastAttr ne '' ) {
178 my $curAttrVal = '';
180 eval "\$curAttrVal = \$$lastAttr";
181 chomp($curAttrVal);
182 chop($curAttrVal);
184 $_ =~ /^\s*(.*)/i;
185 $curAttrVal .= $1;
187 eval "\$$lastAttr = \$curAttrVal";
189 $lastAttr = chkBslash($lastAttr, \$1);
190 next;
193 $lastAttr = '';
194 if (/^\s*label\s*=\s*(.*)/i) {
195 $attrState = 0 if ($attrState < 0);
196 my $newLabel = $1;
198 if ($obj->{'debug'}) {
199 print STDERR qq{
200 $newLabel is duplicated in the attribute file (line $.)
201 } if ($attr{$newLabel});
203 # if $attrState not zero, an unwritten record exists
204 if ($attrState) {
205 $callName = $obj->filterCallName($label,
206 $callName);
207 push(@case, [$case, $format, $comment, $note]);
209 if ($obj->filterLabel($label)) {
210 $attr{$label} =
211 [$callName, $description, $title,
212 $skip, @case];
213 $count++;
215 $format = $description = $title = 'none';
216 $case = $note = $comment = $skip = $callName
217 = '';
218 @case = ();
219 $caseState = 0;
221 $label = $newLabel;
222 $attrState = 1;
224 elsif (/^\s*skip\s*=\s*(.*)/i) {
225 $skip = $1;
227 elsif (/^\s*syscall\s*=\s*(.*)/i) {
228 $callName = $1;
230 elsif (/^\s*program\s*=\s*(.*)/i) {
231 $callName = $1;
233 elsif (/^\s*title\s*=\s*(.*)/i) {
234 $title = $1;
236 elsif (/^\s*see\s*=\s*(.*)/i) {
237 $description = $1;
239 elsif (/^\s*format\s*=\s*(.*)/i) {
240 $format = $1;
241 $lastAttr = chkBslash("format", \$1);
243 elsif (/^\s*comment\s*=\s*(.*)/i) {
244 $comment .= $1;
245 $lastAttr = chkBslash("comment", \$1);
247 elsif (/^\s*note\s*=\s*(.*)/i) {
248 $note .= $1;
249 $lastAttr = chkBslash("note", \$1);
251 elsif (/^\s*case\s*=\s*(.*)/i) {
252 if ($caseState) {
253 push(@case, [$case, $format, $comment, $note]);
254 $format = 'none';
255 $comment = $note = '';
257 $case = $1;
258 $lastAttr = chkBslash("case", \$1);
259 $caseState = 1;
262 if ($attrState) {
263 $callName = $obj->filterCallName($label, $callName);
264 push(@case, [$case, $format, $comment, $note]);
265 if ($obj->filterLabel($label)) {
266 $attr{$label} = [$callName, $description, $title, $skip,
267 @case];
268 $count++;
271 close $fileHandle;
272 print STDERR "found $count audit attribute entries\n" if ($obj->{'debug'});
274 return ($obj->{'attr'} = \%attr, \%token, \%skipClass, \%noteAlias);
277 # readEvent
278 # read eventFile and extract audit event information, including
279 # which classes are associated with each event and what call is
280 # related.
282 sub readEvent {
283 my $obj = shift;
285 my %event = ();
286 my $file = $obj->{'eventFile'};
288 my $fileHandle = do {local *FileHandle; *FileHandle};
289 open($fileHandle, $file) or die sprintf("$failedOpen\n", $file, $!);
291 my $count = 0;
293 unless (defined $obj->{'class'} && (scalar keys %{$obj->{'class'}} > 1)) {
294 $obj->readClass();
297 my @classFilterMasks = ();
298 my $classFilter = $obj->{'classFilter'};
299 if ($classFilter) {
300 foreach (split(',', $classFilter)) {
301 push @classFilterMasks, $obj->{'class'}{$_};
304 # ignore customer-supplied audit events (id > 32767)
306 while (<$fileHandle>) {
307 chomp;
308 s/#.*//; # remove comment
309 next if (/^\s*$/);
310 if (/^\s*(\d+):(\w+):([^:]+):(.*)/) {
311 my $id = $1;
312 my $label = $2;
313 my $description = $3;
314 my $class = $4;
316 if ($id !~ /\d+/) {
317 print STDERR "$id is not numeric (line $.)\n";
318 next;
320 next if ($id > 32767);
322 $class =~ s/\s*$//;
324 if ($obj->{'debug'}) {
325 print STDERR qq{
326 $label is duplicated in the event file (line $.)
327 } if ($event{$label});
329 next unless ($obj->filterLabel($label));
330 my $mask = 0;
331 if ($classFilter) {
332 foreach (split(/\s*,\s*/, $class)) {
333 $mask |= $obj->{'class'}{$_};
335 my $skip = 0;
336 foreach my $filterMask (@classFilterMasks) {
337 unless ($mask & $filterMask) {
338 $skip = 1;
339 last;
342 next if $skip;
344 if ($obj->{'idFilter'}) {
345 next unless ($obj->{'idFilter'} == $id);
347 $event{$label} = [$id, $class, $description];
349 $count++;
352 close $fileHandle;
353 print STDERR "found $count audit events\n" if ($obj->{'debug'});
355 return ($obj->{'event'} = \%event);
358 # readClass
359 # read classFile and extract audit class information
361 sub readClass {
362 my $obj = shift;
364 my %class = ();
365 my $file = $obj->{'classFile'};
367 my $fileHandle = do {local *FileHandle; *FileHandle};
368 open($fileHandle, $file) or die sprintf("$failedOpen\n", $file, $!);
370 my $count = 0;
372 while (<$fileHandle>) {
373 chomp;
374 s/#.*//; # remove comment
375 next if (/^\s*$/);
376 my ($mask1, $class) = split(/:/); # third field not used
377 my $mask2 = hex($mask1); # integer
378 $class{$class} = $mask2;
379 $count++;
381 close $fileHandle;
382 print STDERR "found $count audit classes\n" if ($obj->{'debug'});
384 return ($obj->{'class'} = \%class);
387 sub filterLabel {
388 my $obj = shift;
389 my $label = shift;
391 my $eventFilter = $obj->{'eventFilter'};
392 my $keepIt = 1;
394 $keepIt = 0 if ($eventFilter && ($label !~ /$eventFilter/i));
396 return ($keepIt);
399 # Normally, the root of the event label is the system call. The
400 # attrFile attribute syscall or program overrides this.
402 sub filterCallName {
403 my $obj = shift;
404 my $label = shift;
405 my $callName = shift;
407 return ($callName) if ($callName);
409 $label =~ /AUE_(.*)/;
411 my $name = $1;
413 return (lc ($name));
416 # readControl
417 # read controlFile and extract flags and naflags information
418 # at present, minfree, maxfree and the audit partitions are not
419 # checked.
421 sub readControl {
422 my $obj = shift;
423 my $failMode = shift;
425 my $cError = 0;
426 my $errors = '';
427 my $file = $obj->{'controlFile'};
428 my $invalidClass = gettext('invalid class, %s, in audit_control: %s');
430 my $fileHandle = do {local *FileHandle; *FileHandle};
431 unless (open($fileHandle, $file)) {
432 die sprintf("$failedOpen\n", $file, $!)
433 unless ($failMode eq 'ignore');
434 return (0, '');
436 my %class = $obj->{'class'};
437 my @paths = $obj->{'paths'};
438 while (<$fileHandle>) {
439 chomp;
440 s/#.*//; # remove comment
441 next if (/^\s*$/);
442 if ((/^\s*flags:/i) || (/^\s*naflags:/i)) {
443 my ($class) = /flags:\s*(.*)/;
444 my @class = split(/\s*,\s*/, $class);
446 foreach $class (@class) {
447 $class =~ s/^[-+^]+//;
448 unless (defined ($class{$class})) {
449 $errors .=
450 sprintf("$invalidClass\n",
451 $class, $_);
452 $cError++;
456 elsif (/^\s*dir:\s*(.*)/) {
457 push (@paths, $1);
458 $obj->{'havePath'} = 1;
461 close $fileHandle;
462 return ($cError, $errors);
465 sub getPathList {
466 my $obj = shift;
468 $obj->readControl() unless ($obj->{'havePath'});
470 return ($obj->{'paths'});
473 # readUser
474 # read userFile and extract audit information for validation
476 sub readUser {
477 my $obj = shift;
478 my $failMode = shift;
480 my $cError = 0;
481 my $error = '';
482 my $file = $obj->{'userFile'};
484 my $fileHandle = do {local *FileHandle; *FileHandle};
485 unless (open($fileHandle, $file)) {
486 die sprintf("$failedOpen\n", $file, $!)
487 unless ($failMode eq 'ignore');
488 return (0, '');
490 # these strings are defined here mostly to avoid indentation problems
491 my $emptyErr = gettext('empty audit mask in audit_user: %s');
492 my $syntaxErr1 = gettext(
493 'incorrect syntax (exactly two colons req\'d) in audit_user: %s');
494 my $syntaxErr2 = gettext('incorrect syntax in audit_user: %s');
495 my $invalidErr = gettext('invalid class, %s, in audit_user: %s');
496 my $undefined = gettext('undefined user name in audit_user: %s');
498 my %class = $obj->{'class'};
499 while (<$fileHandle>) {
500 chomp;
501 s/#.*//; # remove comment
502 next if (/^\s*$/);
503 my $colonCount = tr/:/:/;
505 if ($colonCount != 2) {
506 $error .= sprintf("$syntaxErr1\n", $_);
507 $cError++;
509 my ($user, $always, $never) = split(/\s*:\s*/);
510 unless (defined($user)) {
511 $error .= sprintf("$syntaxErr2\n", $_);
512 $cError++;
513 next;
515 $error .= sprintf("$emptyErr\n", $_) unless ($always);
517 my ($name) = getpwnam($user);
518 unless (defined($name)) {
519 $error .= sprintf("$undefined\n", $user);
520 $cError++;
522 unless (defined($always) && defined($never)) {
523 $error .= sprintf("$emptyErr\n", $_);
524 $cError++;
525 next;
527 my $verify = $always . ',' . $never;
528 my @class = split(/\s*,\s*/, $verify);
529 my $thisClass;
531 foreach $thisClass (@class) {
532 $thisClass =~ s/^[-+^]+//;
533 unless (defined $class{$thisClass}) {
534 $error .= sprintf("$invalidErr\n", $thisClass,
535 $_);
536 $cError++;
540 close $fileHandle;
541 return ($cError, $error);
544 # ckAttrEvent complains if controlFile and attrFile don''t contain the
545 # same list of events.
547 sub ckAttrEvent {
548 my $obj = shift;
550 my $cError = 0;
551 my $error = '';
552 my $cAttr = 0;
553 my $label;
554 my $attrErr = gettext(
555 '%s entry in attribute file but not in event file');
556 my $eventErr = gettext(
557 '%s entry in event file but not in attribute file');
559 my %attr = %{$obj->{'attr'}};
560 my %event = %{$obj->{'event'}};
561 foreach $label (keys %attr) {
562 $cAttr++;
563 unless ($event{$label}) {
564 $error .= sprintf("$attrErr\n", $label);
565 $cError++;
568 my $cEvent = 0;
569 foreach $label (keys %event) {
570 $cEvent++;
571 unless ($attr{$label}) {
572 $error .= sprintf("$eventErr\n", $label);
573 $cError++;
576 # debug only; not I18N'd
577 print STDERR
578 "$cAttr audit_record_attr entries and $cEvent audit_event entries\n"
579 if ($obj->{'debug'});
580 return ($cError, $error);
583 # chkBslash (helper)
584 # check the given string for backslash character at the end; if found
585 # return the string sent as a first argument, otherwise return empty
586 # string.
587 sub chkBslash ($$) {
588 my $retStr = shift;
589 my $strPtr = shift;
591 if ( $$strPtr !~ /\\$/ ) {
592 $retStr = '';
595 return $retStr;