From 5049a3a494a6024884ad37dece90d11aee094837 Mon Sep 17 00:00:00 2001 From: hjp Date: Mon, 20 Nov 2006 15:08:21 +0000 Subject: [PATCH] Fixed check of metadata. Should have approximately the functionality of my rsync-based script, except that it doesn't clean up yet. --- lib/Simba/CA.pm | 105 +++++++++++++++++++++++++++++++++++++++++++----- t/01_ca.t | 52 ++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 9 deletions(-) diff --git a/lib/Simba/CA.pm b/lib/Simba/CA.pm index 9cdcdfd..fc99b33 100644 --- a/lib/Simba/CA.pm +++ b/lib/Simba/CA.pm @@ -12,6 +12,7 @@ use Digest::SHA1; use List::Util qw(min); use IO::Handle; use File::stat; +use Scalar::Util qw(tainted); Readonly my $BUFSIZE => 128 * 1024; @@ -46,6 +47,7 @@ sub backup2disk { my @dirs = glob($self->{basedir} . '/????-??-??T??.??.??/' . $target->{host} . '/' . $target->{dir}); $self->{last_backup} = $dirs[-1]; + $self->{last_backup} = $1 if $self->{last_backup} =~ /(.*)/; # detaint my $timestamp = $self->{timestamp} || strftime('%Y-%m-%dT%H.%M.%S', localtime); $self->{this_backup} = $self->{basedir} . "/$timestamp/" . $target->{host} . '/' . $target->{dir}; @@ -93,6 +95,7 @@ sub backup2disk { $size -= length($buffer); $sha1->add($buffer); } + close($file_bfd); my $trailer = <$file_dfd>; # should be empty line $trailer = <$file_dfd>; if ($trailer =~ /^fail /) { @@ -104,6 +107,7 @@ sub backup2disk { } else { print STDERR "unexpected trailer $trailer\n"; } + $self->setmeta($f); } else { print STDERR "unexpected header $header\n"; } @@ -111,7 +115,8 @@ sub backup2disk { } elsif ($f->{t} eq 'd') { my $d = "$self->{this_backup}/$f->{name}"; $d =~ s,//+,/,g; - mkdir_p($d, 0700) or die "cannot mkdir $d: $!"; # XXX + mkdir_p($d) or die "cannot mkdir $d: $!"; # XXX + $self->setmeta($f); } else { # create local copy (or insert into DB only?) print STDERR "ignored $_\n"; @@ -135,6 +140,7 @@ sub parse { $f->{o} = unquote($f->{o}); $f->{g} = unquote($f->{g}); $f->{acl} = unquote($f->{acl}); + $f->{m} = $1 if $f->{m} =~ /^(\d+)$/; return $f; } @@ -144,14 +150,11 @@ sub present { return unless $self->{last_backup}; my $st = lstat("$self->{last_backup}/$f->{name}"); return unless $st; - if ($st->mtime == $f->{m} && - $st->size == $f->{s} && - uid2name($st->uid) eq $f->{o} && - uid2name($st->gid) eq $f->{g} && - mode2acl($st->mode) eq $f->{acl} && - (($st->mode & 04000) == ($f->{setuid} || 0) * 04000) && - (($st->mode & 02000) == ($f->{setgid} || 0) * 02000) && - (($st->mode & 01000) == ($f->{sticky} || 0) * 01000) + if ($st->mtime == $f->{m} && + $st->size == $f->{s} && + $st->uid == name2uid($f->{o}) && + $st->gid == name2gid($f->{g}) && + ($st->mode & 07777) == $self->acl2mode($f) ) { return 1; } else { @@ -199,5 +202,89 @@ sub add_target { return $self->{targets}; } +my %permstrbits = ( + '---' => 0, + '--x' => 1, + '-w-' => 2, + '-wx' => 3, + 'r--' => 4, + 'r-x' => 5, + 'rw-' => 6, + 'rwx' => 7, +); + +sub setmeta { + my ($self, $f) = @_; + my $fn = "$self->{this_backup}/$f->{name}"; + print STDERR "$fn is tainted!" if tainted($fn); + my $mode = $self->acl2mode($f); + print STDERR "$mode is tainted!" if tainted($mode); + chmod($mode, $fn); + chown(name2uid($f->{o}), name2gid($f->{g}), $fn); + utime(time, $f->{m}, $fn); +} + +# computes the mode from the acl (and the set[ug]id and sticky bits) +# and returns it. Optional ACL entries are currently ignored but should +# eventually be returned as a second value. + +sub acl2mode { + my ($self, $f) = @_; + + my $mode = 0; + if ($f->{acl}) { + for my $ace (split(',', $f->{acl})) { + if ($ace =~ /^u::(...)$/) { + $mode |= ($permstrbits{$1} << 6); + } elsif ($ace =~ /^g::(...)$/) { + $mode |= ($permstrbits{$1} << 3); + } elsif ($ace =~ /^o:(...)$/) { + $mode |= ($permstrbits{$1} << 0); + } else { + $self->log(5, "warning: unknown ACE $ace ignored"); + } + } + } + if ($f->{setuid}) { $mode |= 04000 } + if ($f->{setgid}) { $mode |= 02000 } + if ($f->{sticky}) { $mode |= 01000 } + + return $mode; +} + +my %ucache; +sub name2uid { + my ($uname) = @_; + $uname = $1 if $uname =~ /(.*)/; # detaint + return $ucache{$uname} if (defined $ucache{$uname}); + if ($uname =~ /^\d+$/) { + return $ucache{$uname} = $uname; + } else { + my $uid = getpwnam($uname); + if (defined($uid)) { + return $ucache{$uname} = $uid; + } else { + return $ucache{$uname} = -2; + } + } +} + +my %gcache; +sub name2gid { + my ($gname) = @_; + $gname = $1 if $gname =~ /(.*)/; # detaint + return $gcache{$gname} if (defined $gcache{$gname}); + if ($gname =~ /^\d+$/) { + return $gcache{$gname} = $gname; + } else { + my $gid = getgrnam($gname); + if (defined($gid)) { + return $gcache{$gname} = $gid; + } else { + return $gcache{$gname} = -2; + } + } +} + # vim: tw=0 expandtab 1; diff --git a/t/01_ca.t b/t/01_ca.t index fba7b41..1203672 100644 --- a/t/01_ca.t +++ b/t/01_ca.t @@ -9,3 +9,55 @@ BEGIN { use_ok( 'Simba::CA' ); } my $ca = Simba::CA->new(); ok($ca, 'new CA'); +my $uid; +$uid = Simba::CA::name2uid('root'); +ok(defined($uid), 'root has uid'); +cmp_ok($uid, '==', 0, 'root has correct uid'); + +$uid = Simba::CA::name2uid('bin'); +ok(defined($uid), 'bin has uid'); +my $bin_uid; +{ + open(my $fd, '<', '/etc/passwd'); + while (<$fd>) { + if (/^bin:[^:]*:(\d+):/) { + $bin_uid = $1; + } + } +} +cmp_ok($uid, '==', $bin_uid, 'bin has correct uid'); + +$uid = Simba::CA::name2uid('daemon'); +ok(defined($uid), 'daemon has uid'); +my $daemon_uid; +{ + open(my $fd, '<', '/etc/passwd'); + while (<$fd>) { + if (/^daemon:[^:]*:(\d+):/) { + $daemon_uid = $1; + } + } +} +cmp_ok($uid, '==', $daemon_uid, 'daemon has correct uid'); + +$uid = Simba::CA::name2uid('4711'); +ok(defined($uid), 'numerical "user name" has uid'); +cmp_ok($uid, '==', 4711, 'numerical "user name" has correct uid'); + +$uid = Simba::CA::name2uid('i.am.quite.sure.that.this.is.not.a.real.username'); +ok(defined($uid), 'unknown user has uid'); +cmp_ok($uid, '==', -2, 'unknown user has correct uid'); + +my $gid = Simba::CA::name2gid('adm'); +ok(defined($gid), 'adm has gid'); +my $adm_gid; +{ + open(my $fd, '<', '/etc/group'); + while (<$fd>) { + if (/^adm:[^:]*:(\d+):/) { + $adm_gid = $1; + } + } +} +cmp_ok($gid, '==', $adm_gid, 'adm has correct gid'); +