diff options
-rw-r--r-- | TODO | 5 | ||||
-rw-r--r-- | perl/Wallet/Config.pm | 107 | ||||
-rw-r--r-- | perl/Wallet/Object/Keytab.pm | 285 | ||||
-rw-r--r-- | perl/Wallet/Schema.pm | 26 | ||||
-rw-r--r-- | perl/t/data/README | 17 | ||||
-rwxr-xr-x | perl/t/keytab.t | 121 | ||||
-rwxr-xr-x | perl/t/schema.t | 2 |
7 files changed, 528 insertions, 35 deletions
@@ -6,13 +6,8 @@ Required to replace leland_srvtab: enctype attribute on the object and include the enctypes in the object show display. -* Add an attribute indicating that the principal should be synchronized - with Kerberos v4 and include it in the object show display. - * Implement creation of srvtabs from keytabs in the wallet client. -* Add kasetkey support to the keytab object backend. - * Write new files atomically in the wallet client and save backups unless told not to (write to file.new, link the old file to file.old, and do an atomic rename). diff --git a/perl/Wallet/Config.pm b/perl/Wallet/Config.pm index 9a1f9db..c52cfd1 100644 --- a/perl/Wallet/Config.pm +++ b/perl/Wallet/Config.pm @@ -170,17 +170,6 @@ client. =over 4 -=item KEYTAB_CACHE - -Specifies the ticket cache to use when retrieving existing keytabs from the -KDC. This is only used to implement support for the C<unchanging> flag. -The ticket cache must be for a principal with access to run C<keytab -retrieve> via remctl on KEYTAB_REMCTL_HOST. - -=cut - -our $KEYTAB_CACHE; - =item KEYTAB_FILE Specifies the keytab to use to authenticate to B<kadmind>. The principal @@ -256,6 +245,44 @@ default to the local realm. our $KEYTAB_REALM; +=item KEYTAB_TMP + +A directory into which the wallet can write keytabs temporarily while +processing C<get> commands from clients. The keytabs are written into this +directory with predictable names, so this should not be a system temporary +directory such as F</tmp> or F</var/tmp>. It's best to create a directory +solely for this purpose that's owned by the user the wallet server will run +as. + +KEYTAB_TMP must be set to use keytab objects. + +=cut + +our $KEYTAB_TMP; + +=back + +=head2 Retrieving Existing Keytabs + +The keytab object backend optionally supports retrieving existing keys, and +hence keytabs, for Kerberos principals by contacting the KDC via remctl and +talking to B<keytab-backend>. This is enabled by setting the C<unchanging> +flag on keytab objects. To configure that support, set the following +variables. + +=over 4 + +=item KEYTAB_REMCTL_CACHE + +Specifies the ticket cache to use when retrieving existing keytabs from the +KDC. This is only used to implement support for the C<unchanging> flag. +The ticket cache must be for a principal with access to run C<keytab +retrieve> via remctl on KEYTAB_REMCTL_HOST. + +=cut + +our $KEYTAB_CACHE; + =item KEYTAB_REMCTL_HOST The host to which to connect with remctl to retrieve existing keytabs. This @@ -289,20 +316,60 @@ will be used. our $KEYTAB_REMCTL_PORT; -=item KEYTAB_TMP +=back -A directory into which the wallet can write keytabs temporarily while -processing C<get> commands from clients. The keytabs are written into this -directory with predictable names, so this should not be a system temporary -directory such as F</tmp> or F</var/tmp>. It's best to create a directory -solely for this purpose that's owned by the user the wallet server will run -as. +=head2 Synchronization with AFS kaserver -KEYTAB_TMP must be set to use keytab objects. +The keytab backend optionally supports synchronizing keys between the +Kerberos v5 realm and a Kerberos v4 realm using kaserver. This +synchronization is done using B<kasetkey> and is controlled by the C<sync> +attribute on keytab objects. To configure that support, set the following +variables. + +=over 4 + +=item $KEYTAB_AFS_ADMIN + +The Kerberos v4 principal to use for authentication to the AFS kaserver. If +this principal is not in the default local Kerberos v4 realm, it must be +fully qualified. A srvtab for this principal must be stored in the path set +in $KEYTAB_AFS_SRVTAB. This principal must have the ADMIN flag set in the +AFS kaserver so that it can create and remove principals. This variable +must be set to use the kaserver synchronization support. =cut -our $KEYTAB_TMP; +our $KEYTAB_AFS_ADMIN; + +=item $KEYTAB_AFS_KASETKEY + +The path to the B<kasetkey> command-line client. The default value is +C<kasetkey>, which will cause the wallet to search for B<kasetkey> on its +default PATH. + +=cut + +our $KEYTAB_AFS_KASETKEY = 'kasetkey'; + +=item $KEYTAB_AFS_REALM + +The name of the Kerberos v4 realm with which to synchronize keys. This is a +realm, not a cell, so it should be in all uppercase. If this variable is +not set, the default is the realm determined from the local cell name. + +=cut + +our $KEYTAB_AFS_REALM; + +=item $KEYTAB_AFS_SRVTAB + +The path to a srvtab used to authenticate to the AFS kaserver. This srvtab +should be for the principal set in $KEYTAB_AFS_ADMIN. This variable must be +set to use the kaserver synchronization support. + +=cut + +our $KEYTAB_AFS_SRVTAB; =back diff --git a/perl/Wallet/Object/Keytab.pm b/perl/Wallet/Object/Keytab.pm index 582f78c..64e7ce1 100644 --- a/perl/Wallet/Object/Keytab.pm +++ b/perl/Wallet/Object/Keytab.pm @@ -172,6 +172,199 @@ sub kadmin_delprinc { } ############################################################################## +# AFS kaserver synchronization +############################################################################## + +# Given a Kerberos v5 principal name, convert it to a Kerberos v4 principal +# name. Returns undef if it can't convert the name for some reason (right +# now, only if the principal has more than two parts). Note that this mapping +# does not guarantee a unique result; multiple hosts in different domains can +# be mapped to the same Kerberos v4 principal name using this function. +sub kaserver_name { + my ($self, $k5) = @_; + my %host = map { $_ => 1 } qw(host ident imap pop smtp); + $k5 =~ s/\@.*//; + my @parts = split ('/', $k5); + if (@parts == 1) { + return $parts[0]; + } elsif (@parts > 2) { + return undef; + } elsif ($host{$parts[0]}) { + $parts[1] =~ s/\..*//; + $parts[0] = 'rcmd' if $parts[0] eq 'host'; + } + my $k4 = join ('.', @parts); + if ($Wallet::Config::KEYTAB_AFS_REALM) { + $k4 .= '@' . $Wallet::Config::KEYTAB_AFS_REALM; + } + return $k4; +} + +# 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 +# there is no DES key in the keytab. +sub kaserver_srvtab { + my ($self, $keytab, $k5, $srvtab, $k4) = @_; + + # Gah. Someday I will write Perl bindings for Kerberos that are less + # broken. + eval { require Authen::Krb5 }; + if ($@) { + $self->error ("kaserver synchronization support not available: $@"); + return undef; + } + eval { Authen::Krb5::init_context() }; + if ($@ and not $@ =~ /^Authen::Krb5 already initialized/) { + $self->error ('Kerberos initialization failed'); + return undef; + } + undef $@; + + # Do the interface dance. We call kt_read_service_key with 0 for the kvno + # to get any kvno, which works with MIT Kerberos at least. Assume a DES + # enctype of 1. This code won't work with any enctype other than + # des-cbc-crc. + my $princ = Authen::Krb5::parse_name ($k5); + unless (defined $princ) { + my $error = Authen::Krb5::error(); + $self->error ("cannot parse $k5: $error"); + return undef; + } + my $key = Authen::Krb5::kt_read_service_key ($keytab, $princ, 0, 1); + unless (defined $key) { + my $error = Authen::Krb5::error(); + $self->error ("cannot find des-cbc-crc key in $keytab: $error"); + return undef; + } + unless (open (SRVTAB, '>', $srvtab)) { + $self->error ("cannot create $srvtab: $!"); + return undef; + } + + # srvtab format is nul-terminated name, nul-terminated instance, + # nul-terminated realm, single character kvno (which we always set to 0), + # and DES keyblock. + my ($principal, $realm) = split ('@', $k4); + my ($name, $inst) = split (/\./, $principal, 2); + my $data = join ("\0", $name, $inst, $realm); + $data .= "\0\0" . $key->contents; + print SRVTAB $data; + unless (close SRVTAB) { + unlink $srvtab; + $self->error ("cannot write to $srvtab: $!"); + return undef; + } + return 1; +} + +# Given a principal name and a path to the keytab, synchronizes the key with a +# principal in an AFS kaserver. Returns true on success and false on failure. +# 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; + } + my $k4 = $self->kaserver_name ($principal); + if (not defined $k4) { + $self->error ("cannot convert $principal to Kerberos v4"); + return undef; + } + my $srvtab = $Wallet::Config::KEYTAB_TMP . "/srvtab.$$"; + unless ($self->kaserver_srvtab ($keytab, $principal, $srvtab, $k4)) { + return undef; + } + my $pid = open (KASETKEY, '-|'); + if (not defined $pid) { + $self->error ("cannot fork: $!"); + 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; +} + +# 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 { + my ($self, $user, $host, $time) = @_; + $time ||= time; + my @trace = ($user, $host, $time); + my $name = $self->{name}; + eval { + my $sql = "select ks_name from keytab_sync where ks_name = ? and + ks_target = 'kaserver'"; + my $result = $self->{dbh}->selectrow_array ($sql, undef, $name); + if ($result) { + die "kaserver synchronization already set\n"; + } + $sql = "insert into keytab_sync (ks_name, ks_target) + values (?, 'kaserver')"; + $self->{dbh}->do ($sql, undef, $name); + $self->log_set ('type_data sync', undef, 'kaserver', @trace); + $self->{dbh}->commit; + }; + if ($@) { + $self->error ($@); + $self->{dbh}->rollback; + return undef; + } + return 1; +} + +# Clear the kaserver sync attribute. Called by attr(). Returns true on +# success and false on failure, setting the object error if it fails. +sub kaserver_clear { + my ($self, $user, $host, $time) = @_; + $time ||= time; + my @trace = ($user, $host, $time); + my $name = $self->{name}; + eval { + my $sql = "select ks_name from keytab_sync where ks_name = ? and + ks_target = 'kaserver'"; + my $result = $self->{dbh}->selectrow_array ($sql, undef, $name); + unless ($result) { + die "kaserver synchronization not set\n"; + } + $sql = 'delete from keytab_sync where ks_name = ?'; + $self->{dbh}->do ($sql, undef, $name); + $self->log_set ('type_data sync', 'kaserver', undef, @trace); + $self->{dbh}->commit; + }; + if ($@) { + $self->error ($@); + $self->{dbh}->rollback; + return undef; + } + return 1; +} + +############################################################################## # Keytab retrieval ############################################################################## @@ -183,7 +376,7 @@ sub kadmin_delprinc { sub keytab_retrieve { my ($self, $keytab) = @_; my $host = $Wallet::Config::KEYTAB_REMCTL_HOST; - unless ($host and $Wallet::Config::KEYTAB_CACHE) { + unless ($host and $Wallet::Config::KEYTAB_REMCTL_CACHE) { $self->error ('keytab unchanging support not configured'); return undef; } @@ -195,7 +388,7 @@ sub keytab_retrieve { if ($Wallet::Config::KEYTAB_REALM) { $keytab .= '@' . $Wallet::Config::KEYTAB_REALM; } - local $ENV{KRB5CCNAME} = $Wallet::Config::KEYTAB_CACHE; + local $ENV{KRB5CCNAME} = $Wallet::Config::KEYTAB_REMCTL_CACHE; my $port = $Wallet::Config::KEYTAB_REMCTL_PORT; my $principal = $Wallet::Config::KEYTAB_REMCTL_PRINCIPAL; my @command = ('keytab', 'retrieve', $keytab); @@ -218,6 +411,49 @@ sub keytab_retrieve { # Core methods ############################################################################## +# Override attr to support setting the sync attribute. +sub attr { + my ($self, $attribute, $values, $user, $host, $time) = @_; + undef $self->{error}; + if ($attribute ne 'sync') { + $self->error ("unknown attribute $attribute"); + return; + } + if ($values) { + if (@$values > 1) { + $self->error ('only one synchronization target supported'); + return; + } elsif (@$values and $values->[0] ne 'kaserver') { + $self->error ("unsupported synchronization target $values->[0]"); + return; + } + $time ||= time; + my @trace = ($user, $host, $time); + if (@$values) { + return $self->kaserver_set ($user, $host, $time); + } else { + return $self->kaserver_clear ($user, $host, $time); + } + } else { + my @targets; + eval { + my $sql = 'select ks_target from keytab_sync where ks_name = ? + order by ks_target'; + my $sth = $self->{dbh}->prepare ($sql); + $sth->execute ($self->{name}); + my $target; + while (defined ($target = $sth->fetchrow_array)) { + push (@targets, $target); + } + }; + if ($@) { + $self->error ($@); + return; + } + return @targets; + } +} + # Override create to start by creating the principal in Kerberos and only # create the entry in the database if that succeeds. Error handling isn't # great here since we don't have a way to communicate the error back to the @@ -262,6 +498,7 @@ sub get { return undef; } my $file = $Wallet::Config::KEYTAB_TMP . "/keytab.$$"; + unlink $file; return undef if not $self->kadmin_ktadd ($self->{name}, $file); local *KEYTAB; unless (open (KEYTAB, '<', $file)) { @@ -275,9 +512,16 @@ sub get { if ($!) { my $princ = $self->{name}; $self->error ("error reading keytab for principal $princ: $!"); + unlink $file; return undef; } close KEYTAB; + my @sync = $self->attr ('sync'); + if (grep { $_ eq 'kaserver' } @sync) { + unless ($self->kaserver_sync ($self->{name}, $file)) { + return undef; + } + } unlink $file; $self->log_action ('get', $user, $host, $time); return $data; @@ -331,6 +575,43 @@ methods that are overridden or behave specially for this implementation. =over 4 +=item attr(ATTRIBUTE [, VALUES, PRINCIPAL, HOSTNAME [, DATETIME]]) + +Sets or retrieves a given object attribute. The following attributes are +supported: + +=over 4 + +=item sync + +Sets the external systems to which the key of a given principal is +synchronized. The only supported value for this attribute is C<kaserver>, +which says to synchronize the key with an AFS Kerberos v4 kaserver. + +If this attribute is set on a keytab, whenever get() is called for that +keytab, the new DES key will be extracted from that keytab and set in the +configured AFS kaserver. The Kerberos v4 principal name will be the same as +the Kerberos v5 principal name except that the components are separated by +C<.> instead of C</>; the second component is truncated after the first C<.> +if the first component is one of C<host>, C<ident>, C<imap>, C<pop>, or +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. + +=back + +If no other arguments besides ATTRIBUTE are given, returns the values of +that attribute, if any, as a list. On error, returns the empty list. To +distinguish between an error and an empty return, call error() afterwards. +It is guaranteed to return undef unless there was an error. + +If other arguments are given, sets the given ATTRIBUTE values to VALUES, +which must be a reference to an array (even if only one value is being set). +Pass a reference to an empty array to clear the attribute values. +PRINCIPAL, HOSTNAME, and DATETIME are stored as history information. +PRINCIPAL should be the user who is destroying the object. If DATETIME +isn't given, the current time is used. + =item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) This is a class method and should be called on the Wallet::Object::Keytab diff --git a/perl/Wallet/Schema.pm b/perl/Wallet/Schema.pm index d856433..352f497 100644 --- a/perl/Wallet/Schema.pm +++ b/perl/Wallet/Schema.pm @@ -322,7 +322,25 @@ oh_by stores the authenticated identity that made the change, oh_from stores the host from which they made the change, and oh_on stores the time the change was made. -=head2 Storage Backend Data +=head2 Keytab Backend Data + +The keytab backend supports synchronizing keys with an external system. The +permitted external systems are listed in a normalization table: + + create table sync_targets + (st_name varchar(255) primary key); + insert into sync_targets (st_name) values ('kaserver'); + +and then the synchronization targets for a given keytab are stored in this +table: + + create table keytab_sync + (ks_name varchar(255) + not null references objects(ob_name), + ks_target varchar(255) + not null references sync_targets(st_name), + primary key (ks_name, ks_target)); + create index ks_name on keytab_sync (ks_name); The keytab backend supports restricting the allowable enctypes for a given keytab. The permitted enctypes are listed in a normalization table: @@ -333,12 +351,12 @@ keytab. The permitted enctypes are listed in a normalization table: and then the restrictions for a given keytab are stored in this table: create table keytab_enctypes - (ke_principal varchar(255) + (ke_name varchar(255) not null references objects(ob_name), ke_enctype varchar(255) not null references enctypes(en_name), - primary key (ke_principal, ke_enctype)); - create index ke_principal on keytab_enctypes (ke_principal); + primary key (ke_name, ke_enctype)); + create index ke_name on keytab_enctypes (ke_name); To use this functionality, you will need to populate the enctypes table with the enctypes that a keytab may be restricted to. Currently, there is no diff --git a/perl/t/data/README b/perl/t/data/README index 33ec32f..968ec6c 100644 --- a/perl/t/data/README +++ b/perl/t/data/README @@ -27,3 +27,20 @@ and <realm> is the Kerberos realm. Again, I do not recommend using a production realm; the test doesn't need a production realm and it's more secure to stick to a test realm. + +In order to test the AFS kaserver synchronization, you will need to grant +the test processes access to a principal with ADMIN rights in a test AFS +kaserver. This should not be pointed at a production cell! Create the +following files: + + test.admin Fully-qualified principal of ADMIN user + test.cell AFS kaserver test cell + +The ADMIN user will be parsed to determine the default realm for +principals created in the kaserver. You cannot use cross-realm +authentication for this test. This AFS kaserver Kerberos v4 realm will +also need to be configured in your local krb.conf (but not krb.realms). + +The test process will create the principals wallet.one and wallet.two and +on success will clean up after itself. If the test fails, they may be +left behind in the AFS kaserver. diff --git a/perl/t/keytab.t b/perl/t/keytab.t index 7b9a067..d90699c 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 => 66; +use Test::More tests => 96; use Wallet::Config; use Wallet::Object::Keytab; @@ -12,7 +12,7 @@ use Wallet::Server; # Use a local SQLite database for testing. $Wallet::Config::DB_DRIVER = 'SQLite'; $Wallet::Config::DB_INFO = 'wallet-db'; -unlink 'wallet-db'; +unlink ('wallet-db', 'krb5cc_temp', 'krb5cc_test', 'test-acl', 'test-pid'); # Some global defaults to use. my $user = 'admin@EXAMPLE.COM'; @@ -96,6 +96,21 @@ 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 { @@ -111,6 +126,24 @@ sub valid { return $result; } +# Given a Wallet::Object::Keytab object, the keytab data, the Kerberos v5 +# principal, and the Kerberos v4 principal, write the keytab to a file, +# generate a srvtab, and try authenticating using k4start. +sub valid_srvtab { + my ($object, $keytab, $k5, $k4) = @_; + open (KEYTAB, '>', 'keytab') or die "cannot create keytab: $!\n"; + print KEYTAB $keytab; + close KEYTAB; + unless ($object->kaserver_srvtab ('keytab', $k5, 'srvtab', $k4)) { + warn "cannot write srvtab: ", $object->error, "\n"; + return 0; + } + $ENV{KRBTKFILE} = 'krb4cc_temp'; + system ("k4start -f srvtab $k4 2>&1 >/dev/null </dev/null"); + unlink 'keytab', 'srvtab', 'krb4cc_temp'; + return ($? == 0) ? 1 : 0; +} + # Start remctld with the appropriate options to run our fake keytab backend. sub spawn_remctld { my ($path, $principal, $keytab) = @_; @@ -351,7 +384,7 @@ SKIP: { is ($one->get (@trace), undef, 'Get without configuration fails'); is ($one->error, 'keytab unchanging support not configured', ' with the right error'); - $Wallet::Config::KEYTAB_CACHE = 'krb5cc_test'; + $Wallet::Config::KEYTAB_REMCTL_CACHE = 'krb5cc_test'; is ($one->get (@trace), undef, ' and still fails without host'); is ($one->error, 'keytab unchanging support not configured', ' with the right error'); @@ -377,5 +410,87 @@ SKIP: { stop_remctld; } +# 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'; + + # Set up our configuration. + $Wallet::Config::KEYTAB_FILE = 't/data/test.keytab'; + $Wallet::Config::KEYTAB_PRINCIPAL = contents ('t/data/test.principal'); + $Wallet::Config::KEYTAB_REALM = contents ('t/data/test.realm'); + $Wallet::Config::KEYTAB_TMP = '.'; + $Wallet::Config::KEYTAB_AFS_KASETKEY = '../kasetkey/kasetkey'; + my $realm = $Wallet::Config::KEYTAB_REALM; + my $k5 = "wallet/one\@$realm"; + + # Create an object for testing and set the sync attribute. + my $one = eval { + Wallet::Object::Keytab->create ('keytab', 'wallet/one', $dbh, @trace) + }; + ok (defined ($one), 'Creating wallet/one succeeds'); + is ($one->attr ('foo', [ 'bar' ], @trace), undef, + 'Setting unknown attribute fails'); + is ($one->error, 'unknown attribute foo', ' with the right error'); + my @targets = $one->attr ('foo'); + is (scalar (@targets), 0, ' and getting an unknown attribute fails'); + is ($one->error, 'unknown attribute foo', ' with the right error'); + is ($one->attr ('sync', [ 'foo' ], @trace), undef, + ' and setting an unknown sync target fails'); + is ($one->error, 'unsupported synchronization target foo', + ' with the right error'); + is ($one->attr ('sync', [ 'kaserver', 'bar' ], @trace), undef, + ' and setting two targets fails'); + is ($one->error, 'only one synchronization target supported', + ' with the right error'); + is ($one->attr ('sync', [ 'kaserver' ], @trace), 1, + ' but setting only kaserver works'); + @targets = $one->attr ('sync'); + is (scalar (@targets), 1, ' and now one target is set'); + is ($targets[0], 'kaserver', ' and it is correct'); + is ($one->error, undef, ' and there is no error'); + + # Finally, we can test. + is ($one->get (@trace), undef, 'Get without configuration fails'); + is ($one->error, 'kaserver synchronization not configured', + ' with the right error'); + $Wallet::Config::KEYTAB_AFS_ADMIN = contents ('t/data/test.admin'); + my $k4_realm = $Wallet::Config::KEYTAB_AFS_ADMIN; + $k4_realm =~ s/^[^\@]+\@//; + $Wallet::Config::KEYTAB_AFS_REALM = $k4_realm; + my $k4 = "wallet.one\@$k4_realm"; + is ($one->get (@trace), undef, ' and still fails with just admin'); + is ($one->error, 'kaserver synchronization not configured', + ' with the right error'); + $Wallet::Config::KEYTAB_AFS_SRVTAB = 't/data/test.srvtab'; + my $keytab = $one->get (@trace); + if (defined ($keytab)) { + ok (1, ' and now get works'); + } else { + is ($one->error, '', ' and now get works'); + } + ok (valid_srvtab ($one, $keytab, $k5, $k4), ' and the srvtab is valid'); + ok (! -f "./srvtab.$$", ' and the temporary file was cleaned up'); + + # Now remove the sync attribute and make sure things aren't synced. + is ($one->attr ('sync', [], @trace), 1, 'Clearing sync works'); + @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'); + + # Put it back and make sure it works again. + is ($one->attr ('sync', [ 'kaserver' ], @trace), 1, 'Setting sync works'); + $keytab = $one->get (@trace); + ok (defined ($keytab), ' and get works'); + ok (valid_srvtab ($one, $keytab, $k5, $k4), ' and the srvtab is valid'); + + # Destroy the principal. + is ($one->destroy (@trace), 1, 'Destroying wallet/one works'); +} + # Clean up. unlink ('wallet-db', 'krb5cc_temp', 'krb5cc_test', 'test-acl', 'test-pid'); diff --git a/perl/t/schema.t b/perl/t/schema.t index 6829700..02993ae 100755 --- a/perl/t/schema.t +++ b/perl/t/schema.t @@ -13,7 +13,7 @@ ok (defined $schema, 'Wallet::Schema creation'); ok ($schema->isa ('Wallet::Schema'), ' and class verification'); my @sql = $schema->sql; ok (@sql > 0, 'sql() returns something'); -is (scalar (@sql), 22, ' and returns the right number of statements'); +is (scalar (@sql), 26, ' and returns the right number of statements'); # Create a SQLite database to use for create. unlink 'wallet-db'; |