#! /usr/bin/perl -w # integrit - file integrity verification system # Copyright (C) 2006 Ed L. Cashin # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # $Header: /cvsroot/integrit/integrit/examples/viewreport,v 1.14 2006/04/07 09:41:13 wavexx Exp $ # extensions based on a patch from # Volker Apelt # added to version 1.6 of this file. use strict; use XML::Grove; use XML::Grove::Builder; use XML::Parser::PerlSAX; use Tk; if ("debug" eq "yup") { use Data::Dumper; } &go; sub go { &show_excuses; push @ARGV, "-" if ! @ARGV; # default to stdin foreach my $fname (@ARGV) { &show_report($fname); } } sub show_excuses { print STDERR <<"MY_BOLOGNA_HAS_A_FIRST_NAME"; viewreport - an example of a GUI viewer for the XML reports that integrit generates. I'm not too much of a GUI programmer, so I'm sure you can do better! MY_BOLOGNA_HAS_A_FIRST_NAME # ' # for emacs (in case single quote in here doc are odd) } sub show_report { die "Error: parameter missing" if @_ < 1; my ($fname) = @_; #--------------use perl for XML parsing my $xml = &slirp_file($fname); my $gbuilder = new XML::Grove::Builder; my $parser = new XML::Parser::PerlSAX('Handler' => $gbuilder); my $doc = $parser->parse('Source' => { 'String' => $xml, }); die "Error: unable to parse XML" unless $doc; my $report = &get_report($doc); # print new Data::Dumper([ $report ])->Dump and die; # debug #--------------use Perl/Tk for the GUI my $mw = new MainWindow(-title => "demo integrit report viewer"); my $date = localtime($report->{'Attributes'}{'date'}); $mw->Label(-text => "report from $date")->pack; $mw->Button(-text => 'Quit', -command => [ $mw => 'destroy' ])->pack; my $box = $mw->Listbox(-relief => 'sunken', -width => -1, # Shrink to fit -height => 5, -setgrid => 1); $box->bind('' => sub { &show_info($_[0], $report); }); &insert_elements($box, $report); my $scroll = $mw->Scrollbar(-command => ['yview', $box]); $box->configure(-yscrollcommand => ['set', $scroll]); $box->pack(-side => 'left', -fill => 'both', -expand => 1); $scroll->pack(-side => 'right', -fill => 'y'); MainLoop(); } #--------------show more info for a given report line sub show_info { die "Error: missing parameter" if @_ < 2; my ($mw, $report) = @_; my $target = $mw->get('active'); foreach my $elem (@{ $report->{'Contents'} }) { my $name = $elem->{'Name'}; my $elem_data = &elem_data($elem); next unless $name; if (($name eq "change") && ($target =~ m/^change/)) { my $type = $elem->{'Attributes'}{'type'}; my $filename = $elem->{'Attributes'}{'file'}; my $tag = "$name ($type): $filename"; if ($tag eq $target) { &display_change($mw, $elem); } } elsif (($name eq "options") && ($target eq "options")) { &display_options($mw, $elem); } elsif ($target eq "$name: $elem_data"){ &display_misc($mw, $elem); } } } #-----------------show changed data sub display_change { die "Error: missing parameter" if @_ < 2; my ($mw, $elem) = @_; my $type = $elem->{'Attributes'}{'type'}; # e.g. stat, SHA-1 my $filename = $elem->{'Attributes'}{'file'}; my ($old, $new) = ("(unknown)", "(unknown)"); my $change = $elem->{'Contents'}[0]; # $type .= ": " . $change->{'Name'}; my $changew = $mw->Toplevel(-title => "viewreport: change"); # new window $changew->Label(-text => "file: $filename")->pack; $changew->Label(-text => "type of change: $type")->pack; foreach my $change (@{ $elem->{'Contents'} }) { if ($type eq "stat") { my $statkind = $change->{'Name'}; # e.g., mtime, atime my $postprocess = &postprocessor_for_statchange($statkind); $changew->Label(-text => "stat change: $statkind")->pack; foreach my $stat (@{ $change->{'Contents'} }) { my $name = $stat->{'Name'}; if (($name eq "old") || ($name eq "new")) { my $val = $postprocess->(&elem_data($stat)); $changew->Label(-text => "$name: $val")->pack; } } } else { my $name = $change->{'Name'}; if (($name eq "old") || ($name eq "new")) { my $val = &elem_data($change); $changew->Label(-text => "$name: $val")->pack; } } } $changew->Button(-text => "Dismiss", -command => [ $changew => 'destroy' ])->pack; } sub display_options { die "Error: missing parameter" if @_ < 2; my ($mw, $elem) = @_; #-----------new window my $optionsw = $mw->Toplevel(-title => "viewreport: options"); foreach my $thang (@{ $elem->{'Contents'} }) { my $name = $thang->{'Name'}; next unless $name; my $val = &elem_data($thang); $optionsw->Label(-text => "$name: $val")->pack; } $optionsw->Button(-text => "Dismiss", -command => [ $optionsw => 'destroy' ])->pack; } sub display_misc { die "Error: missing parameter" if @_ < 2; my ($mw, $elem) = @_; my $name = $elem->{'Name'}; return unless $name; #-----------new window my $miscw = $mw->Toplevel(-title => "viewreport: misc"); my $val = &elem_data($elem); $miscw->Label(-text => "$name: $val")->pack if $val =~ /\S/; $miscw->Button(-text => "Dismiss", -command => [ $miscw => 'destroy' ])->pack; } sub get_report { die "Error: missing parameter" if @_ < 1; my ($doc) = @_; my $report; foreach my $elem (@{ $doc->{'Contents'} }) { if ($elem->{'Name'} eq "report") { $report = $elem; } } die "no report found" unless $report; return $report; } sub insert_elements { die "Error: missing parameter" if @_ < 2; my ($box, $report) = @_; foreach my $elem (@{ $report->{'Contents'} }) { my $name = $elem->{'Name'}; next unless $name; if ($name eq "change") { my $type = $elem->{'Attributes'}{'type'}; my $filename = $elem->{'Attributes'}{'file'}; $box->insert('end', "$name ($type): $filename"); } elsif ($name eq "options") { $box->insert('end', $name); } else { $box->insert('end', "$name: " . &elem_data($elem)); } } } sub elem_data { die "Error: missing parameter" if @_ < 1; my ($thang) = @_; my $contents = $thang->{'Contents'} or return "(no data)"; foreach my $elem (@$contents) { next unless $elem->{'Data'}; return $elem->{'Data'}; } return "(no data)"; } sub slirp_file { die "Error: missing parameter" if @_ < 1; my ($self, $filename) = @_; # disgard self param if (! ref $self) { #--------assume it's not an OO method if there's no # reference in the first param. $filename = $self; } open SLIRPFILE, $filename or die "Error: could not open $filename for reading"; local $/ = undef; my $file_contents = ; close SLIRPFILE; return $file_contents; } ####### element post processors # turn raw element data into readable text sub postprocessor_for_statchange { die "Error: missing parameter" if @_ < 1; my ($statchange) = @_; if ($statchange =~ m/_time$/) { return \&post_processor_time2localtime; } elsif ( $statchange eq 'gid' ){ return \&post_processor_gid; } elsif ( $statchange eq 'uid' ){ return \&post_processor_uid; } return \&post_processor_noop; } sub post_processor_noop { return $_[0]; } sub post_processor_time2localtime { my $time = shift; # time in unix seconds since .... return localtime($time); } sub post_processor_gid { my $gid = shift; # numeric gid if( $gid !~ /^\d+/ ){ return "$gid"; } my $name = getgrgid($gid); # must be scalar! if( ! defined $name ){ return "(unknown)$gid"; } return $name; } sub post_processor_uid { my $uid = shift; # numeric gid if( $uid !~ /^\d+/ ){ return "$uid"; } my $name = getpwuid($uid); # must be scalar! if( ! defined $name ){ return "(unknown)$uid"; } return $name; } ####### end element post processors