diff options
Diffstat (limited to 'perl/Wallet')
| -rw-r--r-- | perl/Wallet/Config.pm | 83 | ||||
| -rw-r--r-- | perl/Wallet/Object/Keytab.pm | 349 | ||||
| -rw-r--r-- | perl/Wallet/Schema.pm | 10 | 
3 files changed, 59 insertions, 383 deletions
| diff --git a/perl/Wallet/Config.pm b/perl/Wallet/Config.pm index 3f52cf0..7198c07 100644 --- a/perl/Wallet/Config.pm +++ b/perl/Wallet/Config.pm @@ -1,7 +1,7 @@  # Wallet::Config -- Configuration handling for the wallet server.  #  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University  #  # See LICENSE for licensing terms. @@ -14,7 +14,7 @@ use vars qw($PATH $VERSION);  # This version should be increased on any code change to this module.  Always  # use two digits for the minor version with a leading zero if necessary so  # that it will sort properly. -$VERSION = '0.03'; +$VERSION = '0.04';  # Path to the config file to load.  $PATH = $ENV{WALLET_CONFIG} || '/etc/wallet/wallet.conf'; @@ -351,85 +351,6 @@ our $KEYTAB_REMCTL_PORT;  =back -=head2 Synchronization with AFS kaserver - -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_AFS_ADMIN; - -=item KEYTAB_AFS_DESTROY - -If this variable, which is false by default, is set to a true value, each -time a keytab object that is not configured to be synchronized with the AFS -kaserver, the corresponding Kerberos v4 principal will be deleted from the -AFS kaserver.  Use this with caution; it will cause the AFS kaserver realm -to be slowly stripped of principals.  This is intended for use with -migration from Kerberos v4 to Kerberos v5, where the old principals should -be deleted out of Kerberos v4 whenever not requested from the wallet to aid -in tracking down and removing any systems with lingering Kerberos v4 -dependencies. - -Be aware that multiple Kerberos v5 principals map to the same Kerberos v4 -principal since in Kerberos v4 the domain name is stripped from the -principal for machine principals.  If you create a keytab named -host/foo.example.com and mark it synchronized, and then create another -keytab named host/foo.example.net and don't mark it synchronized, -downloading the second will destroy the Kerberos v4 principal of the first -if this variable is set. - -=cut - -our $KEYTAB_AFS_DESTROY; - -=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 -  =head1 NETDB ACL CONFIGURATION  These configuration variables are only needed if you intend to use the diff --git a/perl/Wallet/Object/Keytab.pm b/perl/Wallet/Object/Keytab.pm index 9fece80..b604907 100644 --- a/perl/Wallet/Object/Keytab.pm +++ b/perl/Wallet/Object/Keytab.pm @@ -1,7 +1,8 @@  # Wallet::Object::Keytab -- Keytab object implementation for the wallet.  #  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2007, 2008, 2009 Board of Trustees, Leland Stanford Jr. University +# Copyright 2007, 2008, 2009, 2010 +#     Board of Trustees, Leland Stanford Jr. University  #  # See LICENSE for licensing terms. @@ -24,230 +25,7 @@ use Wallet::Kadmin;  # This version should be increased on any code change to this module.  Always  # use two digits for the minor version with a leading zero if necessary so  # that it will sort properly. -$VERSION = '0.07'; - -############################################################################## -# 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 > 2) { -        return; -    } elsif (@parts == 2 and $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; -} - -# 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; -    } -    my $pid = open (KASETKEY, '-|'); -    if (not defined $pid) { -        $self->error ("cannot fork: $!"); -        return; -    } elsif ($pid == 0) { -        # Don't use die here; it will get trapped as an exception.  Also be -        # careful about our database handles.  (We still lose if there's some -        # other database handle open we don't know about.) -        $self->{dbh}->{InactiveDestroy} = 1; -        unless (open (STDERR, '>&STDOUT')) { -            warn "cannot redirect stderr: $!\n"; -            exit 1; -        } -        unless (exec ($kasetkey, '-k', $admin_srvtab, '-a', $admin, @args)) { -            warn "cannot exec $kasetkey: $!\n"; -            exit 1; -        } -    } 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; -        } -    } -    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 -# 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; -    } -    eval { Authen::Krb5::init_context() }; -    if ($@ and not $@ =~ /^Authen::Krb5 already initialized/) { -        $self->error ('Kerberos initialization failed'); -        return; -    } -    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; -    } -    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; -    } -    unless (open (SRVTAB, '>', $srvtab)) { -        $self->error ("cannot create $srvtab: $!"); -        return; -    } - -    # 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); -    $realm ||= ''; -    my ($name, $inst) = split (/\./, $principal, 2); -    $inst ||= ''; -    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; -    } -    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) = @_; -    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; -    } -    my $srvtab = $Wallet::Config::KEYTAB_TMP . "/srvtab.$$"; -    unless ($self->kaserver_srvtab ($keytab, $principal, $srvtab, $k4)) { -        return; -    } -    unless ($self->kaserver_kasetkey ('-c', $srvtab, '-s', $k4)) { -        unlink $srvtab; -        return; -    } -    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; -    } -    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 { -    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; -    } -    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; -    } -    return 1; -} +$VERSION = '0.08';  ##############################################################################  # Enctype restriction @@ -379,9 +157,14 @@ sub keytab_retrieve {  # Core methods  ############################################################################## -# Override attr to support setting the enctypes and sync attributes. +# Override attr to support setting the enctypes and sync attributes.  Note +# that the sync attribute has no supported targets at present and hence will +# always return an error, but the code is still here so that it doesn't have +# to be rewritten once a new sync target is added.  sub attr {      my ($self, $attribute, $values, $user, $host, $time) = @_; +    $time ||= time; +    my @trace = ($user, $host, $time);      my %known = map { $_ => 1 } qw(enctypes sync);      undef $self->{error};      unless ($known{$attribute}) { @@ -395,14 +178,25 @@ sub attr {              if (@$values > 1) {                  $self->error ('only one synchronization target supported');                  return; -            } elsif (@$values and $values->[0] ne 'kaserver') { +            } elsif (@$values) {                  my $target = $values->[0];                  $self->error ("unsupported synchronization target $target");                  return; -            } elsif (@$values) { -                return $self->kaserver_set ($user, $host, $time);              } else { -                return $self->kaserver_clear ($user, $host, $time); +                eval { +                    my $sql = 'select ks_target from keytab_sync where +                        ks_name = ?'; +                    my $dbh = $self->{dbh}; +                    my $name = $self->{name}; +                    my ($result) = $dbh->selectrow_array ($sql, undef, $name); +                    if ($result) { +                        my $sql = 'delete from keytab_sync where ks_name = ?'; +                        $self->{dbh}->do ($sql, undef, $name); +                        $self->log_set ('type_data sync', $result, undef, +                                        @trace); +                    } +                    $self->{dbh}->commit; +                }              }          }      } else { @@ -511,12 +305,6 @@ 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; -        } -    }      eval {          my $sql = 'delete from keytab_sync where ks_name = ?';          $self->{dbh}->do ($sql, undef, $self->{name}); @@ -582,15 +370,6 @@ sub get {          return;      }      close KEYTAB; -    my @sync = $self->attr ('sync'); -    if (grep { $_ eq 'kaserver' } @sync) { -        unless ($self->kaserver_sync ($self->{name}, $file)) { -            unlink $file; -            return; -        } -    } elsif ($Wallet::Config::KEYTAB_AFS_DESTROY) { -        $self->kaserver_destroy ($self->{name}); -    }      unlink $file;      $self->log_action ('get', $user, $host, $time);      return $data; @@ -646,7 +425,7 @@ methods that are overridden or behave specially for this implementation.  =item attr(ATTRIBUTE [, VALUES, PRINCIPAL, HOSTNAME [, DATETIME]]) -Sets or retrieves a given object attribute.  The following attributes are +Sets or retrieves a given object attribute.  The following attribute is  supported:  =over 4 @@ -655,40 +434,21 @@ supported:  Restricts the generated keytab to a specific set of encryption types.  The  values of this attribute must be enctype strings recognized by Kerberos -(strings like C<aes256-cts> or C<des-cbc-crc>).  Encryption types must also -be present in the list of supported enctypes stored in the database database -or the attr() method will reject them.  Note that the salt should not be -included; since the salt is irrelevant for keytab keys, it will always be -set to C<normal> by the wallet. +(strings like C<aes256-cts-hmac-sha1-96> or C<des-cbc-crc>).  Encryption +types must also be present in the list of supported enctypes stored in the +database database or the attr() method will reject them.  Note that the +salt should not be included; since the salt is irrelevant for keytab keys, +it will always be set to the default by the wallet. -If this attribute is set, the specified enctype list will be passed to -ktadd when get() is called for that keytab.  If it is not set, the default -set in the KDC will be used. +If this attribute is set, the principal will be restricted to that +specific enctype list when get() is called for that keytab.  If it is not +set, the default set in the KDC will be used.  This attribute is ignored if the C<unchanging> flag is set on a keytab.  Keytabs retrieved with C<unchanging> set will contain all keys present in  the KDC for that Kerberos principal and therefore may contain different  enctypes than those requested by this attribute. -=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. - -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 @@ -716,11 +476,11 @@ used.  When a new keytab object is created, the Kerberos principal designated by  NAME is also created in the Kerberos realm determined from the wallet -configuration.  If the principal already exists, create() still succeeds (so -that a previously unmanaged principal can be imported into the wallet). -Otherwise, if the Kerberos principal could not be created, create() fails. -The principal is created with the C<-randkey> option to randomize its keys. -NAME must not contain the realm; instead, the KEYTAB_REALM configuration +configuration.  If the principal already exists, create() still succeeds +(so that a previously unmanaged principal can be imported into the +wallet).  Otherwise, if the Kerberos principal could not be created, +create() fails.  The principal is created with the randomized keys.  NAME +must not contain the realm; instead, the KEYTAB_REALM configuration  variable should be set.  See Wallet::Config(3) for more information.  If create() fails, it throws an exception. @@ -738,18 +498,14 @@ destroying the object.  If DATETIME isn't given, the current time is used.  =item get(PRINCIPAL, HOSTNAME [, DATETIME]) -Retrieves a keytab for this object and returns the keytab data or undef -on error.  The caller should call error() to get the error message if -get() returns undef.  The keytab is created with C<ktadd>, invalidating -any existing keytabs for that principal, unless the unchanging flag is set -on the object.  PRINCIPAL, HOSTNAME, and DATETIME are stored as history -information.  PRINCIPAL should be the user who is downloading the keytab. -If DATETIME isn't given, the current time is used. - -If the configuration variable $KEYTAB_AFS_DESTROY is set and the C<sync> -attribute is not set to C<kaserver>, calling get() on a keytab object will -cause the corresponding Kerberos v4 principal to be destroyed.  This -variable is not set by default. +Retrieves a keytab for this object and returns the keytab data or undef on +error.  The caller should call error() to get the error message if get() +returns undef.  The keytab is created with new randomized keys, +invalidating any existing keytabs for that principal, unless the +unchanging flag is set on the object.  PRINCIPAL, HOSTNAME, and DATETIME +are stored as history information.  PRINCIPAL should be the user who is +downloading the keytab.  If DATETIME isn't given, the current time is +used.  =back @@ -767,15 +523,14 @@ of the current process.  The file is unlinked after being read.  =head1 LIMITATIONS -Currently, this implementation only supports MIT Kerberos and needs -modifications to support Heimdal.  It calls an external B<kadmin> program -rather than using a native Perl module and therefore requires B<kadmin> be -installed and parses its output.  It may miss some error conditions if the -output of B<kadmin> ever changes. +Currently, when used with MIT Kerberos, this implementation calls an +external B<kadmin> program rather than using a native Perl module and +therefore requires B<kadmin> be installed and parses its output.  It may +miss some error conditions if the output of B<kadmin> ever changes.  Only one Kerberos realm is supported for a given wallet implementation and -all keytab objects stored must be in that realm.  Keytab names in the wallet -database do not have realm information. +all keytab objects stored must be in that realm.  Keytab names in the +wallet database do not have realm information.  =head1 SEE ALSO diff --git a/perl/Wallet/Schema.pm b/perl/Wallet/Schema.pm index 2b256a2..252da03 100644 --- a/perl/Wallet/Schema.pm +++ b/perl/Wallet/Schema.pm @@ -1,7 +1,7 @@  # Wallet::Schema -- Database schema for the wallet system.  #  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University  #  # See LICENSE for licensing terms. @@ -20,7 +20,7 @@ use DBI;  # This version should be increased on any code change to this module.  Always  # use two digits for the minor version with a leading zero if necessary so  # that it will sort properly. -$VERSION = '0.05'; +$VERSION = '0.06';  ##############################################################################  # Data manipulation @@ -372,12 +372,12 @@ change was made.  =head2 Keytab Backend Data -The keytab backend supports synchronizing keys with an external system.  The -permitted external systems are listed in a normalization table: +The keytab backend has stub support for synchronizing keys with an +external system, although no external systems are currently supported. +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: | 
