From: Robert 'phaylon' Sedlacek Date: Mon, 7 May 2012 19:34:08 +0000 (+0000) Subject: Sudoers probe to read /etc/sudoers files X-Git-Tag: v0.001_001~127 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=478358f55bd35ad098cceb2bf676960994fb99b5;p=scpubgit%2FSystem-Introspector.git Sudoers probe to read /etc/sudoers files --- diff --git a/lib/System/Introspector/Sudoers.pm b/lib/System/Introspector/Sudoers.pm new file mode 100644 index 0000000..d6bfca6 --- /dev/null +++ b/lib/System/Introspector/Sudoers.pm @@ -0,0 +1,60 @@ +package System::Introspector::Sudoers; +use Moo; + +has sudoers_file => ( + is => 'ro', + default => sub { '/etc/sudoers' }, +); + +has hostname => ( + is => 'ro', + default => sub { scalar `hostname` }, +); + +sub gather { + my ($self) = @_; + my %file = $self->_gather_files($self->sudoers_file); + return \%file; +} + +sub _gather_files { + my ($self, $file) = @_; + open my $fh, '<', $file + or return $file => { error => "Unable to read: $!" }; + my @lines = <$fh>; + my %file = ($file => { body => join '', @lines }); + for my $line (@lines) { + chomp $line; + if ($line =~ m{^#include\s+(.+)$}) { + my $inc_file = $self->_insert_hostname($1); + %file = (%file, $self->_gather_files($inc_file)); + } + elsif ($line =~ m{^#includedir\s+(.+)$}) { + my $inc_dir = $self->_insert_hostname($1); + %file = (%file, $self->_gather_from_dir($inc_dir)); + } + } + return %file; +} + +sub _gather_from_dir { + my ($self, $dir) = @_; + opendir(my $dh, $dir); + return $dir => { error => "Unable to read dir $dir: $!" } + unless $dh; + my %file; + while (my $file = readdir $dh) { + next if $file =~ m{\.} or $file =~ m{~$}; + %file = (%file, $self->_gather_files("$dir/$file")); + } + return %file; +} + +sub _insert_hostname { + my ($self, $value) = @_; + my $hostname = $self->hostname; + $value =~ s{\%h}{$hostname}g; + return $value; +} + +1; diff --git a/t/sudoers.t b/t/sudoers.t new file mode 100644 index 0000000..8420e07 --- /dev/null +++ b/t/sudoers.t @@ -0,0 +1,51 @@ +use strictures 1; +use Test::More; +use FindBin; + +use System::Introspector::Sudoers; + +my $dir = "$FindBin::Bin/sudoers-data"; + +system("mkdir $dir"); +system("mkdir $dir/bar_host"); + +my $start = write_file('sudoers', + 'foo bar', + 'baz qux', + "#include $dir/foo_%h", + "#includedir $dir/bar_%h", +); + +my $foo_file = write_file('foo_host', + 'in foo', +); +my $bar_file = write_file("bar_host/baz", + 'in bar file', +); + +my $probe = System::Introspector::Sudoers->new( + sudoers_file => $start, + hostname => 'host', +); + +ok((my $data = $probe->gather), 'received data'); + +my $inc = "#include $dir/foo_\%h\n#includedir $dir/bar_\%h\n"; +is_deeply $data, { + $start => { body => "foo bar\nbaz qux\n$inc" }, + $foo_file => { body => "in foo\n" }, + $bar_file => { body => "in bar file\n" }, +}, 'found files'; + +system("rm $_") for $start, $foo_file, $bar_file; +system("rmdir $dir/bar_host"); +system("rmdir $dir"); +done_testing; + +sub write_file { + my ($file, @lines) = @_; + my $path = "$FindBin::Bin/sudoers-data/$file"; + open my $fh, '>', $path or die "Unable to write $path: $!\n"; + print $fh map "$_\n", @lines; + return $path; +}