diff options
-rw-r--r-- | perl/Wallet/Object/Keytab.pm | 86 | ||||
-rwxr-xr-x | perl/t/keytab.t | 39 |
2 files changed, 76 insertions, 49 deletions
diff --git a/perl/Wallet/Object/Keytab.pm b/perl/Wallet/Object/Keytab.pm index 64e7ce1..293e41e 100644 --- a/perl/Wallet/Object/Keytab.pm +++ b/perl/Wallet/Object/Keytab.pm @@ -200,6 +200,40 @@ sub kaserver_name { return $k4; } +# Run kasetkey with the given arguments. Returns true on success and false on +# failure. On failure, sets the internal error to the error from kasetkey. +sub kaserver_kasetkey { + my ($self, @args) = @_; + my $admin = $Wallet::Config::KEYTAB_AFS_ADMIN; + my $admin_srvtab = $Wallet::Config::KEYTAB_AFS_SRVTAB; + my $kasetkey = $Wallet::Config::KEYTAB_AFS_KASETKEY; + unless ($kasetkey and $admin and $admin_srvtab) { + $self->error ('kaserver synchronization not configured'); + return undef; + } + my $pid = open (KASETKEY, '-|'); + if (not defined $pid) { + $self->error ("cannot fork: $!"); + return undef; + } elsif ($pid == 0) { + open (STDERR, '>&STDOUT') or die "cannot redirect stderr: $!\n"; + exec ($kasetkey, '-k', $admin_srvtab, '-a', $admin, @args) + or die "cannot exec $kasetkey: $!\n"; + } else { + local $/; + my $output = <KASETKEY>; + close KASETKEY; + if ($? != 0) { + $output =~ s/\s+\z//; + $output =~ s/\n/, /g; + $output = ': ' . $output if $output; + $self->error ("cannot synchronize key with kaserver$output"); + return undef; + } + } + return 1; +} + # Given a keytab file name, the Kerberos v5 principal that's stored in that # keytab, a srvtab file name, and the corresponding Kerberos v4 principal, # write out a srvtab file containing the DES key in that keytab. Fails if @@ -246,7 +280,9 @@ sub kaserver_srvtab { # nul-terminated realm, single character kvno (which we always set to 0), # and DES keyblock. my ($principal, $realm) = split ('@', $k4); + $realm ||= ''; my ($name, $inst) = split (/\./, $principal, 2); + $inst ||= ''; my $data = join ("\0", $name, $inst, $realm); $data .= "\0\0" . $key->contents; print SRVTAB $data; @@ -263,13 +299,6 @@ sub kaserver_srvtab { # On failure, sets the internal error. sub kaserver_sync { my ($self, $principal, $keytab) = @_; - my $admin = $Wallet::Config::KEYTAB_AFS_ADMIN; - my $admin_srvtab = $Wallet::Config::KEYTAB_AFS_SRVTAB; - my $kasetkey = $Wallet::Config::KEYTAB_AFS_KASETKEY; - unless ($kasetkey and $admin and $admin_srvtab) { - $self->error ('kaserver synchronization not configured'); - return undef; - } if ($Wallet::Config::KEYTAB_REALM) { $principal .= '@' . $Wallet::Config::KEYTAB_REALM; } @@ -282,33 +311,27 @@ sub kaserver_sync { unless ($self->kaserver_srvtab ($keytab, $principal, $srvtab, $k4)) { return undef; } - my $pid = open (KASETKEY, '-|'); - if (not defined $pid) { - $self->error ("cannot fork: $!"); + unless ($self->kaserver_kasetkey ('-c', $srvtab, '-s', $k4)) { unlink $srvtab; return undef; - } elsif ($pid == 0) { - open (STDERR, '>&STDOUT') or die "cannot redirect stderr: $!\n"; - exec ($kasetkey, '-k', $admin_srvtab, '-a', $admin, '-c', $srvtab, - '-s', $k4) - or die "cannot exec $kasetkey: $!\n"; - } else { - local $/; - my $output = <KASETKEY>; - close KASETKEY; - if ($? != 0) { - $output =~ s/\s+\z//; - $output =~ s/\n/, /g; - $output = ': ' . $output if $output; - $self->error ("cannot synchronize key with kaserver$output"); - unlink $srvtab; - return undef; - } } unlink $srvtab; return 1; } +# Given a principal name, destroy the corresponding principal in the AFS +# kaserver. Returns true on success and false on failure, setting the object +# error if it fails. +sub kaserver_destroy { + my ($self, $principal) = @_; + my $k4 = $self->kaserver_name ($principal); + if (not defined $k4) { + $self->error ("cannot convert $principal to Kerberos v4"); + return undef; + } + return $self->kaserver_kasetkey ('-D', $k4); +} + # Set the kaserver sync attribute. Called by attr(). Returns true on success # and false on failure, setting the object error if it fails. sub kaserver_set { @@ -472,6 +495,12 @@ sub destroy { $self->error ("cannot destroy $id: object is locked"); return; } + my @sync = $self->attr ('sync'); + if (grep { $_ eq 'kaserver' } @sync) { + unless ($self->kaserver_destroy ($self->{name})) { + return undef; + } + } return undef if not $self->kadmin_delprinc ($self->{name}); return $self->SUPER::destroy ($user, $host, $time); } @@ -598,6 +627,9 @@ C<smtp>; and the first component is C<rcmd> if the Kerberos v5 principal component is C<host>. The principal name must not contain more than two components. +If this attribute is set, calling destroy() will also destroy the principal +from the AFS kaserver, with a principal mapping determined as above. + =back If no other arguments besides ATTRIBUTE are given, returns the values of diff --git a/perl/t/keytab.t b/perl/t/keytab.t index d90699c..93eea1d 100755 --- a/perl/t/keytab.t +++ b/perl/t/keytab.t @@ -3,7 +3,7 @@ # # t/keytab.t -- Tests for the keytab object implementation. -use Test::More tests => 96; +use Test::More tests => 100; use Wallet::Config; use Wallet::Object::Keytab; @@ -96,21 +96,6 @@ sub created { return (system_quiet ('kvno', $principal) == 0); } -# Check whether a principal exists in the kaserver. Requires that the admin -# and srvtab variables be set up already. -sub created_kaserver { - my ($principal) = @_; - my $admin = $Wallet::Config::KEYTAB_AFS_ADMIN; - my $srvtab = $Wallet::Config::KEYTAB_AFS_SRVTAB; - my $realm = $Wallet::Config::KEYTAB_AFS_REALM; - my ($name, $instance) = split (/\./, $principal); - $ENV{KRBTKFILE} = 'krb4cc_temp'; - system ("k4start -f $srvtab -r $realm -S $name -I $instance $admin" - . " 2>&1 >/dev/null </dev/null"); - unlink 'krb4cc_temp'; - return ($? == 0) ? 1 : 0; -} - # Given keytab data and the principal, write it to a file and try # authenticating using kinit. sub valid { @@ -412,8 +397,8 @@ SKIP: { # Tests for kaserver synchronization support. SKIP: { - skip 'no keytab configuration', 30 unless -f 't/data/test.keytab'; - skip 'no AFS kaserver configuration', 30 unless -f 't/data/test.srvtab'; + skip 'no keytab configuration', 34 unless -f 't/data/test.keytab'; + skip 'no AFS kaserver configuration', 34 unless -f 't/data/test.srvtab'; # Set up our configuration. $Wallet::Config::KEYTAB_FILE = 't/data/test.keytab'; @@ -477,12 +462,20 @@ SKIP: { @targets = $one->attr ('sync'); is (scalar (@targets), 0, ' and now there is no attribute'); is ($one->error, undef, ' and no error'); - $keytab = $one->get (@trace); - ok (defined ($keytab), ' and get still works'); - ok (! valid_srvtab ($one, $keytab, $k5, $k4), ' but the srvtab does not'); - ok (created_kaserver ('wallet.one'), ' and the principal is still there'); + my $new_keytab = $one->get (@trace); + ok (defined ($new_keytab), ' and get still works'); + ok (! valid_srvtab ($one, $new_keytab, $k5, $k4), + ' but the srvtab does not'); + ok (valid_srvtab ($one, $keytab, $k5, $k4), ' and the old one does'); + is ($one->destroy (@trace), 1, ' and destroying wallet/one works'); + ok (valid_srvtab ($one, $keytab, $k5, $k4), + ' and the principal is still there'); # Put it back and make sure it works again. + $one = eval { + Wallet::Object::Keytab->create ('keytab', 'wallet/one', $dbh, @trace) + }; + ok (defined ($one), 'Creating wallet/one succeeds'); is ($one->attr ('sync', [ 'kaserver' ], @trace), 1, 'Setting sync works'); $keytab = $one->get (@trace); ok (defined ($keytab), ' and get works'); @@ -490,6 +483,8 @@ SKIP: { # Destroy the principal. is ($one->destroy (@trace), 1, 'Destroying wallet/one works'); + ok (! valid_srvtab ($one, $keytab, $k5, $k4), + ' and the principal is gone'); } # Clean up. |