diff options
| author | Russ Allbery <eagle@eyrie.org> | 2016-01-17 19:43:10 -0800 | 
|---|---|---|
| committer | Russ Allbery <eagle@eyrie.org> | 2016-01-17 19:43:10 -0800 | 
| commit | 4b3f858ef567c0d12511e7fea2a56f08f2729635 (patch) | |
| tree | e1cad1c445669045b47264c8957878352c7adc03 /perl/lib/Wallet/Object | |
| parent | 7856dc7cc5e16140c0084474fe54338f293bf77e (diff) | |
| parent | 76f93739a8a933d98b87db9496861dae7de0ae1a (diff) | |
Imported Upstream version 1.3upstream/1.3
Diffstat (limited to 'perl/lib/Wallet/Object')
| -rw-r--r-- | perl/lib/Wallet/Object/Base.pm | 21 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo.pm | 164 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo/LDAPProxy.pm | 202 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo/PAM.pm | 205 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo/RDP.pm | 204 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo/RadiusProxy.pm | 204 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/File.pm | 16 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Keytab.pm | 75 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Password.pm | 224 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/WAKeyring.pm | 16 | 
10 files changed, 438 insertions, 893 deletions
| diff --git a/perl/lib/Wallet/Object/Base.pm b/perl/lib/Wallet/Object/Base.pm index bdd61fb..221031f 100644 --- a/perl/lib/Wallet/Object/Base.pm +++ b/perl/lib/Wallet/Object/Base.pm @@ -1,6 +1,7 @@ -# Wallet::Object::Base -- Parent class for any object stored in the wallet. +# Wallet::Object::Base -- Parent class for any object stored in the wallet  #  # Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org>  # Copyright 2007, 2008, 2010, 2011, 2014  #     The Board of Trustees of the Leland Stanford Junior University  # @@ -11,22 +12,17 @@  ##############################################################################  package Wallet::Object::Base; -require 5.006; +use 5.008;  use strict;  use warnings; -use vars qw($VERSION);  use DateTime;  use Date::Parse qw(str2time); -use DBI;  use Text::Wrap qw(wrap);  use Wallet::ACL; -# 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.08'; +our $VERSION = '1.03';  ##############################################################################  # Constructors @@ -609,6 +605,15 @@ sub history {  # The get methods must always be overridden by the subclass.  sub get { die "Do not instantiate Wallet::Object::Base directly\n"; } +# The update method should only work if a subclass supports it as something +# different from get.  That makes it explicit about whether the subclass has +# a meaningful update. +sub update { +    my ($self) = @_; +    $self->error ("update is not supported for this type, use get instead"); +    return; +} +  # Provide a default store implementation that returns an immutable object  # error so that auto-generated types don't have to provide their own.  sub store { diff --git a/perl/lib/Wallet/Object/Duo.pm b/perl/lib/Wallet/Object/Duo.pm index d08294b..1aca979 100644 --- a/perl/lib/Wallet/Object/Duo.pm +++ b/perl/lib/Wallet/Object/Duo.pm @@ -1,7 +1,8 @@  # Wallet::Object::Duo -- Base Duo object implementation for the wallet  #  # Written by Russ Allbery <eagle@eyrie.org> -# Copyright 2014 +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# Copyright 2014, 2015  #     The Board of Trustees of the Leland Stanford Junior University  #  # See LICENSE for licensing terms. @@ -11,25 +12,111 @@  ##############################################################################  package Wallet::Object::Duo; -require 5.006; +use 5.008;  use strict;  use warnings; -use vars qw(@ISA $VERSION);  use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration;  use Perl6::Slurp qw(slurp); -use Wallet::Config (); +use Wallet::Config;  use Wallet::Object::Base; -@ISA = qw(Wallet::Object::Base); +our @ISA     = qw(Wallet::Object::Base); +our $VERSION = '1.03'; + +# Mappings from our types into what Duo calls the integration types. +our %DUO_TYPES = ( +                  'duo'        => { +                      integration => 'unix', +                      output      => \&_output_generic, +                  }, +                  'duo-ldap'   => { +                      integration => 'ldapproxy', +                      output      => \&_output_ldap, +                  }, +                  'duo-pam'    => { +                      integration => 'unix', +                      output      => \&_output_pam, +                  }, +                  'duo-radius' => { +                      integration => 'radius', +                      output      => \&_output_radius, +                  }, +                 ); + +# Extra types to add.  These are all just named as the Duo integration name +# with duo- before it and go to the generic output.  Put them here to prevent +# pages of settings.  These are also not all actually set as types in the +# types table to prevent overpopulation.  You should manually create the +# entries in that table for any Duo integrations you want to add. +our @EXTRA_TYPES = ('accountsapi', 'adfs', 'adminapi', 'array', 'barracuda', +                    'cisco', 'citrixcag', 'citrixns', 'confluence', 'drupal', +                    'f5bigip', 'f5firepass', 'fortinet', 'jira', 'juniper', +                    'juniperuac', 'lastpass', 'okta', 'onelogin', 'openvpn', +                    'openvpnas', 'owa', 'paloalto', 'rdgateway', 'rdp', +                    'rdweb', 'rest', 'rras', 'shibboleth', 'sonicwallsra', +                    'splunk', 'tmg', 'uag', 'verify', 'vmwareview', 'websdk', +                    'wordpress'); +for my $type (@EXTRA_TYPES) { +    my $wallet_type = 'duo-'.$type; +    $DUO_TYPES{$wallet_type}{integration} = $type; +    $DUO_TYPES{$wallet_type}{output}      = \&_output_generic; +}; -# 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.02'; +############################################################################## +# Get output methods +############################################################################## + +# Output for any miscellaneous Duo integration, usually those that use a GUI +# to set information and so don't need a custom configuration file. +sub _output_generic { +    my ($key, $secret, $hostname) = @_; + +    my $output; +    $output .= "Integration key: $key\n"; +    $output .= "Secret key:      $secret\n"; +    $output .= "Host:            $hostname\n"; + +    return $output; +} + +# Output for the Duo unix integration, which hooks into the PAM stack. +sub _output_pam { +    my ($key, $secret, $hostname) = @_; + +    my $output = "[duo]\n"; +    $output .= "ikey = $key\n"; +    $output .= "skey = $secret\n"; +    $output .= "host = $hostname\n"; + +    return $output; +} + +# Output for the radius proxy, which can be plugged into the proxy config. +sub _output_radius { +    my ($key, $secret, $hostname) = @_; + +    my $output = "[radius_server_challenge]\n"; +    $output .= "ikey     = $key\n"; +    $output .= "skey     = $secret\n"; +    $output .= "api_host = $hostname\n"; +    $output .= "client   = radius_client\n"; + +    return $output; +} + +# Output for the LDAP proxy, which can be plugged into the proxy config. +sub _output_ldap { +    my ($key, $secret, $hostname) = @_; + +    my $output = "[ldap_server_challenge]\n"; +    $output .= "ikey     = $key\n"; +    $output .= "skey     = $secret\n"; +    $output .= "api_host = $hostname\n"; + +    return $output; +}  ##############################################################################  # Core methods @@ -66,8 +153,20 @@ sub new {      my $key_file = $Wallet::Config::DUO_KEY_FILE;      my $agent    = $Wallet::Config::DUO_AGENT; +    # Check that we can load all of the required modules. +    eval { +        require Net::Duo; +        require Net::Duo::Admin; +        require Net::Duo::Admin::Integration; +    }; +    if ($@) { +        my $error = $@; +        chomp $error; +        1 while ($error =~ s/ at \S+ line \d+\.?\z//); +        die "Duo object support not available: $error\n"; +    } +      # Construct the Net::Duo::Admin object. -    require Net::Duo::Admin;      my $duo = Net::Duo::Admin->new (          {              key_file   => $key_file, @@ -86,7 +185,7 @@ sub new {  # great here since we don't have a way to communicate the error back to the  # caller.  sub create { -    my ($class, $type, $name, $schema, $creator, $host, $time, $duo_type) = @_; +    my ($class, $type, $name, $schema, $creator, $host, $time) = @_;      # We have to have a Duo integration key file set.      if (not $Wallet::Config::DUO_KEY_FILE) { @@ -95,8 +194,26 @@ sub create {      my $key_file = $Wallet::Config::DUO_KEY_FILE;      my $agent    = $Wallet::Config::DUO_AGENT; +    # Make sure this is actually a type we know about, since this handler +    # can handle many types. +    if (!exists $DUO_TYPES{$type}) { +        die "$type is not a valid duo integration\n"; +    } + +    # Check that we can load all of the required modules. +    eval { +        require Net::Duo; +        require Net::Duo::Admin; +        require Net::Duo::Admin::Integration; +    }; +    if ($@) { +        my $error = $@; +        chomp $error; +        1 while ($error =~ s/ at \S+ line \d+\.?\z//); +        die "Duo object support not available: $error\n"; +    } +      # Construct the Net::Duo::Admin object. -    require Net::Duo::Admin;      my $duo = Net::Duo::Admin->new (          {              key_file   => $key_file, @@ -105,8 +222,7 @@ sub create {      );      # Create the object in Duo. -    require Net::Duo::Admin::Integration; -    $duo_type ||= $Wallet::Config::DUO_TYPE; +    my $duo_type = $DUO_TYPES{$type}{integration};      my %data = (          name  => "$name ($duo_type)",          notes => 'Managed by wallet', @@ -201,11 +317,17 @@ sub get {      my $json = JSON->new->utf8 (1)->relaxed (1);      my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); -    # Construct the returned file. -    my $output; -    $output .= "Integration key: $key\n"; -    $output .= 'Secret key:      ' . $integration->secret_key . "\n"; -    $output .= "Host:            $config->{api_hostname}\n"; +    # Construct the returned file.  Assume the generic handler in case there +    # is no valid handler, though that shouldn't happen. +    my $output_sub; +    my $type = $self->{type}; +    if (exists $DUO_TYPES{$type}{output}) { +        $output_sub = $DUO_TYPES{$type}{output}; +    } else { +        $output_sub = \&_output_generic; +    } +    my $output = $output_sub->($key, $integration->secret_key, +                               $config->{api_hostname});      # Log the action and return.      $self->log_action ('get', $user, $host, $time); diff --git a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm deleted file mode 100644 index 74ff43c..0000000 --- a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm +++ /dev/null @@ -1,202 +0,0 @@ -# Wallet::Object::Duo::LDAPProxy -- Duo auth proxy integration for LDAP -# -# Written by Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -#     The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::LDAPProxy; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# 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.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { -    my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - -    $time ||= time; -    my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, -                                      $time, 'ldapproxy'); -    return $self; -} - -# Override get to output the data in a specific format used for Duo LDAP -# integration -sub get { -    my ($self, $user, $host, $time) = @_; -    $time ||= time; - -    # Check that the object isn't locked. -    my $id = $self->{type} . ':' . $self->{name}; -    if ($self->flag_check ('locked')) { -        $self->error ("cannot get $id: object is locked"); -        return; -    } - -    # Retrieve the integration from Duo. -    my $key; -    eval { -        my %search = (du_name => $self->{name}); -        my $row = $self->{schema}->resultset ('Duo')->find (\%search); -        $key = $row->get_column ('du_key'); -    }; -    if ($@) { -        $self->error ($@); -        return; -    } -    my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - -    # We also need the admin server name, which we can get from the Duo object -    # configuration with a bit of JSON decoding. -    my $json = JSON->new->utf8 (1)->relaxed (1); -    my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - -    # Construct the returned file. -    my $output = "[ldap_server_challenge]\n"; -    $output .= "ikey     = $key\n"; -    $output .= 'skey     = ' . $integration->secret_key . "\n"; -    $output .= "api_host = $config->{api_hostname}\n"; - -    # Log the action and return. -    $self->log_action ('get', $user, $host, $time); -    return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab LDAP auth - -=head1 NAME - -Wallet::Object::Duo::LDAPProxy -- Duo auth proxy integration for LDAP - -=head1 SYNOPSIS - -    my @name = qw(duo-ldap host.example.com); -    my @trace = ($user, $host, time); -    my $object = Wallet::Object::Duo::LDAPProxy->create (@name, $schema, @trace); -    my $config = $object->get (@trace); -    $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::LDAPProxy is a representation of Duo -integrations with the wallet, specifically to output Duo integrations -in a format that can easily be pulled into configuring the Duo -Authentication Proxy for LDAP. It implements the wallet object API -and provides the necessary glue to create a Duo integration, return a -configuration file containing the key and API information for that -integration, and delete the integration from Duo when the wallet object -is destroyed. - -The integration information is always returned in the configuration file -format expected by the Authentication Proxy for Duo in configuring it -for LDAP. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations.  To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set.  See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo.  See the -documentation for that class for all generic methods.  Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo.  It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-ldap> and -must be for the rest of the wallet system to use the right class, but -this module doesn't check for ease of subclassing), using DBH as the -handle to the wallet metadata database.  PRINCIPAL, HOSTNAME, and -DATETIME are stored as history information.  PRINCIPAL should be the -user who is creating the object.  If DATETIME isn't given, the current -time is used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object.  If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration.  Returns undef on failure.  The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - -    [ldap_server_challenge] -    ikey     = <integration-key> -    skey     = <secret-key> -    api_host = <api-hostname> - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -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 - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system.  The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Jon Robertson <jonrober@stanford.edu> - -=cut diff --git a/perl/lib/Wallet/Object/Duo/PAM.pm b/perl/lib/Wallet/Object/Duo/PAM.pm deleted file mode 100644 index 6f90ba1..0000000 --- a/perl/lib/Wallet/Object/Duo/PAM.pm +++ /dev/null @@ -1,205 +0,0 @@ -# Wallet::Object::Duo::PAM -- Duo PAM int. object implementation for wallet -# -# Written by Russ Allbery <eagle@eyrie.org> -#            Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -#     The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::PAM; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# 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.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { -    my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - -    $time ||= time; -    my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, -                                      $time, 'unix'); -    return $self; -} - -# Override get to output the data in a specific format used by Duo's PAM -# module. -sub get { -    my ($self, $user, $host, $time) = @_; -    $time ||= time; - -    # Check that the object isn't locked. -    my $id = $self->{type} . ':' . $self->{name}; -    if ($self->flag_check ('locked')) { -        $self->error ("cannot get $id: object is locked"); -        return; -    } - -    # Retrieve the integration from Duo. -    my $key; -    eval { -        my %search = (du_name => $self->{name}); -        my $row = $self->{schema}->resultset ('Duo')->find (\%search); -        $key = $row->get_column ('du_key'); -    }; -    if ($@) { -        $self->error ($@); -        return; -    } -    my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - -    # We also need the admin server name, which we can get from the Duo object -    # configuration with a bit of JSON decoding. -    my $json = JSON->new->utf8 (1)->relaxed (1); -    my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - -    # Construct the returned file. -    my $output = "[duo]\n"; -    $output .= "ikey = $key\n"; -    $output .= 'skey = ' . $integration->secret_key . "\n"; -    $output .= "host = $config->{api_hostname}\n"; - -    # Log the action and return. -    $self->log_action ('get', $user, $host, $time); -    return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab - -=head1 NAME - -Wallet::Object::Duo::PAM -- Duo PAM int. object implementation for wallet - -=head1 SYNOPSIS - -    my @name = qw(duo-pam host.example.com); -    my @trace = ($user, $host, time); -    my $object = Wallet::Object::Duo::PAM->create (@name, $schema, @trace); -    my $config = $object->get (@trace); -    $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::PAM is a representation of Duo integrations with -the wallet, specifically to output Duo integrations in a format that -can easily be pulled into configuring the Duo PAM interface.  It -implements the wallet object API and provides the necessary glue to -create a Duo integration, return a configuration file containing the key -and API information for that integration, and delete the integration from -Duo when the wallet object is destroyed. - -The integration information is always returned in the configuration file -format expected by the Duo UNIX integration.  The results of retrieving -this object will be text, suitable for putting in the UNIX integration -configuration file, containing the integration key, secret key, and admin -hostname for that integration. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations.  To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set.  See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo.  See the -documentation for that class for all generic methods.  Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo.  It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-pam> and must -be for the rest of the wallet system to use the right class, but this -module doesn't check for ease of subclassing), using DBH as the handle -to the wallet metadata database.  PRINCIPAL, HOSTNAME, and DATETIME are -stored as history information.  PRINCIPAL should be the user who is -creating the object.  If DATETIME isn't given, the current time is -used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object.  If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration.  Returns undef on failure.  The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - -    [duo] -    ikey = <integration-key> -    skey = <secret-key> -    host = <api-hostname> - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -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 - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system.  The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Russ Allbery <eagle@eyrie.org> -Jon Robertson <eagle@eyrie.org> - -=cut diff --git a/perl/lib/Wallet/Object/Duo/RDP.pm b/perl/lib/Wallet/Object/Duo/RDP.pm deleted file mode 100644 index 2e975fc..0000000 --- a/perl/lib/Wallet/Object/Duo/RDP.pm +++ /dev/null @@ -1,204 +0,0 @@ -# Wallet::Object::Duo::RDP -- Duo RDP int. object implementation for wallet -# -# Written by Russ Allbery <eagle@eyrie.org> -#            Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -#     The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::RDP; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# 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.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { -    my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - -    $time ||= time; -    my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, -                                      $time, 'rdp'); -    return $self; -} - -# Override get to output the data in a specific format used by Duo's RDP -# module. -sub get { -    my ($self, $user, $host, $time) = @_; -    $time ||= time; - -    # Check that the object isn't locked. -    my $id = $self->{type} . ':' . $self->{name}; -    if ($self->flag_check ('locked')) { -        $self->error ("cannot get $id: object is locked"); -        return; -    } - -    # Retrieve the integration from Duo. -    my $key; -    eval { -        my %search = (du_name => $self->{name}); -        my $row = $self->{schema}->resultset ('Duo')->find (\%search); -        $key = $row->get_column ('du_key'); -    }; -    if ($@) { -        $self->error ($@); -        return; -    } -    my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - -    # We also need the admin server name, which we can get from the Duo object -    # configuration with a bit of JSON decoding. -    my $json = JSON->new->utf8 (1)->relaxed (1); -    my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - -    # Construct the returned file. -    my $output; -    $output .= "Integration key: $key\n"; -    $output .= 'Secret key:      ' . $integration->secret_key . "\n"; -    $output .= "Host:            $config->{api_hostname}\n"; - -    # Log the action and return. -    $self->log_action ('get', $user, $host, $time); -    return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab RDP - -=head1 NAME - -Wallet::Object::Duo::RDP -- Duo RDP int. object implementation for wallet - -=head1 SYNOPSIS - -    my @name = qw(duo-rdp host.example.com); -    my @trace = ($user, $host, time); -    my $object = Wallet::Object::Duo::RDP->create (@name, $schema, @trace); -    my $config = $object->get (@trace); -    $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::RDP is a representation of Duo integrations with -the wallet, specifically to output Duo integrations to set up an RDP -integration.  This can be used to set up remote logins, or all Windows -logins period if so selected in Duo's software.  It implements the -wallet object API and provides the necessary glue to create a Duo -integration, return a configuration file containing the key and API -information for that integration, and delete the integration from Duo -when the wallet object is destroyed. - -Because the Duo RDP software is configured by a GUI, the information -returned for a get operation is a simple set that's readable but not -useful for directly plugging into a config file.  The values would need -to be cut and pasted into the GUI. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations.  To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set.  See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo.  See the -documentation for that class for all generic methods.  Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo.  It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-pam> and must -be for the rest of the wallet system to use the right class, but this -module doesn't check for ease of subclassing), using DBH as the handle -to the wallet metadata database.  PRINCIPAL, HOSTNAME, and DATETIME are -stored as history information.  PRINCIPAL should be the user who is -creating the object.  If DATETIME isn't given, the current time is -used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object.  If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration.  Returns undef on failure.  The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - -    Integration key: <integration-key> -    Secret key:      <secret-key> -    Host:            <api-hostname> - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -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 - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system.  The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Russ Allbery <eagle@eyrie.org> -Jon Robertson <eagle@eyrie.org> - -=cut diff --git a/perl/lib/Wallet/Object/Duo/RadiusProxy.pm b/perl/lib/Wallet/Object/Duo/RadiusProxy.pm deleted file mode 100644 index faa0c2f..0000000 --- a/perl/lib/Wallet/Object/Duo/RadiusProxy.pm +++ /dev/null @@ -1,204 +0,0 @@ -# Wallet::Object::Duo::RadiusProxy -- Duo auth proxy integration for radius -# -# Written by Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -#     The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::RadiusProxy; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# 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.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { -    my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - -    $time ||= time; -    my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, -                                      $time, 'radius'); -    return $self; -} - -# Override get to output the data in a specific format used for Duo radius -# integration -sub get { -    my ($self, $user, $host, $time) = @_; -    $time ||= time; - -    # Check that the object isn't locked. -    my $id = $self->{type} . ':' . $self->{name}; -    if ($self->flag_check ('locked')) { -        $self->error ("cannot get $id: object is locked"); -        return; -    } - -    # Retrieve the integration from Duo. -    my $key; -    eval { -        my %search = (du_name => $self->{name}); -        my $row = $self->{schema}->resultset ('Duo')->find (\%search); -        $key = $row->get_column ('du_key'); -    }; -    if ($@) { -        $self->error ($@); -        return; -    } -    my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - -    # We also need the admin server name, which we can get from the Duo object -    # configuration with a bit of JSON decoding. -    my $json = JSON->new->utf8 (1)->relaxed (1); -    my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - -    # Construct the returned file. -    my $output = "[radius_server_challenge]\n"; -    $output .= "ikey     = $key\n"; -    $output .= 'skey     = ' . $integration->secret_key . "\n"; -    $output .= "api_host = $config->{api_hostname}\n"; -    $output .= "client   = radius_client\n"; - -    # Log the action and return. -    $self->log_action ('get', $user, $host, $time); -    return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab auth - -=head1 NAME - -Wallet::Object::Duo::RadiusProxy -- Duo auth proxy integration for RADIUS - -=head1 SYNOPSIS - -    my @name = qw(duo-radius host.example.com); -    my @trace = ($user, $host, time); -    my $object = Wallet::Object::Duo::RadiusProxy->create (@name, $schema, @trace); -    my $config = $object->get (@trace); -    $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::RadiusProxy is a representation of Duo -integrations with the wallet, specifically to output Duo integrations -in a format that can easily be pulled into configuring the Duo -Authentication Proxy for Radius. It implements the wallet object API -and provides the necessary glue to create a Duo integration, return a -configuration file containing the key and API information for that -integration, and delete the integration from Duo when the wallet object -is destroyed. - -The integration information is always returned in the configuration file -format expected by the Authentication Proxy for Duo in configuring it -for Radius. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations.  To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set.  See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo.  See the -documentation for that class for all generic methods.  Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo.  It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-radius> and -must be for the rest of the wallet system to use the right class, but -this module doesn't check for ease of subclassing), using DBH as the -handle to the wallet metadata database.  PRINCIPAL, HOSTNAME, and -DATETIME are stored as history information.  PRINCIPAL should be the -user who is creating the object.  If DATETIME isn't given, the current -time is used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object.  If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration.  Returns undef on failure.  The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - -    [radius_server_challenge] -    ikey     = <integration-key> -    skey     = <secret-key> -    api_host = <api-hostname> -    client   = radius_client - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -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 - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system.  The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Jon Robertson <jonrober@stanford.edu> - -=cut diff --git a/perl/lib/Wallet/Object/File.pm b/perl/lib/Wallet/Object/File.pm index 226e32c..9452ff4 100644 --- a/perl/lib/Wallet/Object/File.pm +++ b/perl/lib/Wallet/Object/File.pm @@ -1,6 +1,7 @@ -# Wallet::Object::File -- File object implementation for the wallet. +# Wallet::Object::File -- File object implementation for the wallet  #  # Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org>  # Copyright 2008, 2010, 2014  #     The Board of Trustees of the Leland Stanford Junior University  # @@ -11,23 +12,18 @@  ##############################################################################  package Wallet::Object::File; -require 5.006; +use 5.006;  use strict;  use warnings; -use vars qw(@ISA $VERSION);  use Digest::MD5 qw(md5_hex);  use File::Copy qw(move); -use Wallet::Config (); +use Wallet::Config;  use Wallet::Object::Base; -@ISA = qw(Wallet::Object::Base); - -# 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'; +our @ISA     = qw(Wallet::Object::Base); +our $VERSION = '1.03';  ##############################################################################  # File naming diff --git a/perl/lib/Wallet/Object/Keytab.pm b/perl/lib/Wallet/Object/Keytab.pm index 975179b..f276b3f 100644 --- a/perl/lib/Wallet/Object/Keytab.pm +++ b/perl/lib/Wallet/Object/Keytab.pm @@ -1,6 +1,7 @@ -# Wallet::Object::Keytab -- Keytab object implementation for the wallet. +# Wallet::Object::Keytab -- Keytab object implementation for the wallet  #  # Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org>  # Copyright 2007, 2008, 2009, 2010, 2013, 2014  #     The Board of Trustees of the Leland Stanford Junior University  # @@ -11,22 +12,48 @@  ##############################################################################  package Wallet::Object::Keytab; -require 5.006; +use 5.008;  use strict;  use warnings; -use vars qw(@ISA $VERSION); -use Wallet::Config (); -use Wallet::Object::Base; +use Wallet::Config;  use Wallet::Kadmin; +use Wallet::Object::Base; -@ISA = qw(Wallet::Object::Base); +our @ISA     = qw(Wallet::Object::Base); +our $VERSION = '1.03'; -# 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.09'; +############################################################################## +# Shared methods +############################################################################## + +# Generate a keytab into a temporary file and then return that as the return +# value.  Used by both get and update, as the only difference is how we +# handle the unchanging flag. +sub retrieve { +    my ($self, $operation, $user, $host, $time) = @_; +    $time ||= time; +    my $id = $self->{type} . ':' . $self->{name}; +    if ($self->flag_check ('locked')) { +        $self->error ("cannot get $id: object is locked"); +        return; +    } +    my $kadmin = $self->{kadmin}; +    my $result; +    if ($operation eq 'get' && $self->flag_check ('unchanging')) { +        $result = $kadmin->keytab ($self->{name}); +    } else { +        my @enctypes = $self->attr ('enctypes'); +        $result = $kadmin->keytab_rekey ($self->{name}, @enctypes); +    } +    if (defined $result) { +        $self->log_action ($operation, $user, $host, $time); +    } else { +        $self->error ($kadmin->error); +    } +    return $result; +}  ##############################################################################  # Enctype restriction @@ -314,25 +341,15 @@ sub destroy {  # return that as the return value.  sub get {      my ($self, $user, $host, $time) = @_; -    $time ||= time; -    my $id = $self->{type} . ':' . $self->{name}; -    if ($self->flag_check ('locked')) { -        $self->error ("cannot get $id: object is locked"); -        return; -    } -    my $kadmin = $self->{kadmin}; -    my $result; -    if ($self->flag_check ('unchanging')) { -        $result = $kadmin->keytab ($self->{name}); -    } else { -        my @enctypes = $self->attr ('enctypes'); -        $result = $kadmin->keytab_rekey ($self->{name}, @enctypes); -    } -    if (defined $result) { -        $self->log_action ('get', $user, $host, $time); -    } else { -        $self->error ($kadmin->error); -    } +    my $result = $self->retrieve ('get', $user, $host, $time); +    return $result; +} + +# Our update implementation.  Generate a new keytab regardless of the +# unchanging flag. +sub update { +    my ($self, $user, $host, $time) = @_; +    my $result = $self->retrieve ('update', $user, $host, $time);      return $result;  } diff --git a/perl/lib/Wallet/Object/Password.pm b/perl/lib/Wallet/Object/Password.pm new file mode 100644 index 0000000..1db53f3 --- /dev/null +++ b/perl/lib/Wallet/Object/Password.pm @@ -0,0 +1,224 @@ +# Wallet::Object::Password -- Password object implementation for the wallet +# +# Written by Jon Robertson <jonrober@stanford.edu> +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# Copyright 2015 +#     The Board of Trustees of the Leland Stanford Junior University +# +# See LICENSE for licensing terms. + +############################################################################## +# Modules and declarations +############################################################################## + +package Wallet::Object::Password; + +use 5.008; +use strict; +use warnings; + +use Crypt::GeneratePassword qw(chars); +use Digest::MD5 qw(md5_hex); +use Wallet::Config; +use Wallet::Object::File; + +our @ISA     = qw(Wallet::Object::File); +our $VERSION = '1.03'; + +############################################################################## +# File naming +############################################################################## + +# Returns the path into which that password object will be stored or undef on +# error.  On error, sets the internal error. +sub file_path { +    my ($self) = @_; +    my $name = $self->{name}; +    unless ($Wallet::Config::PWD_FILE_BUCKET) { +        $self->error ('password support not configured'); +        return; +    } +    unless ($name) { +        $self->error ('password objects may not have empty names'); +        return; +    } +    my $hash = substr (md5_hex ($name), 0, 2); +    $name =~ s/([^\w-])/sprintf ('%%%02X', ord ($1))/ge; +    my $parent = "$Wallet::Config::PWD_FILE_BUCKET/$hash"; +    unless (-d $parent || mkdir ($parent, 0700)) { +        $self->error ("cannot create password bucket $hash: $!"); +        return; +    } +    return "$Wallet::Config::PWD_FILE_BUCKET/$hash/$name"; +} + +############################################################################## +# Shared methods +############################################################################## + +# Return the contents of the file. +sub retrieve { +    my ($self, $operation, $user, $host, $time) = @_; +    $time ||= time; +    my $id = $self->{type} . ':' . $self->{name}; +    if ($self->flag_check ('locked')) { +        $self->error ("cannot get $id: object is locked"); +        return; +    } +    my $path = $self->file_path; +    return unless $path; + +    # If nothing is yet stored, or we have requested an update, generate a +    # random password and save it to the file. +    my $schema = $self->{schema}; +    my %search = (ob_type => $self->{type}, +                  ob_name => $self->{name}); +    my $object = $schema->resultset('Object')->find (\%search); +    if (!$object->ob_stored_on || $operation eq 'update') { +        unless (open (FILE, '>', $path)) { +            $self->error ("cannot store initial settings for $id: $!\n"); +            return; +        } +        my $pass = chars ($Wallet::Config::PWD_LENGTH_MIN, +                          $Wallet::Config::PWD_LENGTH_MAX); +        print FILE $pass; +        $self->log_action ('store', $user, $host, $time); +        unless (close FILE) { +            $self->error ("cannot get $id: $!"); +            return; +        } +    } + +    unless (open (FILE, '<', $path)) { +        $self->error ("cannot get $id: object has not been stored"); +        return; +    } +    local $/; +    my $data = <FILE>; +    unless (close FILE) { +        $self->error ("cannot get $id: $!"); +        return; +    } +    $self->log_action ($operation, $user, $host, $time); +    return $data; +} + +############################################################################## +# Core methods +############################################################################## + +# Return the contents of the file. +sub get { +    my ($self, $user, $host, $time) = @_; +    my $result = $self->retrieve ('get', $user, $host, $time); +    return $result; +} + +# Return the contents of the file after resetting them to a random string. +sub update { +    my ($self, $user, $host, $time) = @_; +    my $result = $self->retrieve ('update', $user, $host, $time); +    return $result; +} + +1; +__END__ + +############################################################################## +# Documentation +############################################################################## + +=head1 NAME + +Wallet::Object::Password - Password object implementation for wallet + +=for stopwords +API HOSTNAME DATETIME keytab remctld backend nul Allbery wallet-backend + +=head1 SYNOPSIS + +    my @name = qw(file mysql-lsdb) +    my @trace = ($user, $host, time); +    my $object = Wallet::Object::Password->create (@name, $schema, @trace); +    unless ($object->store ("the-password\n")) { +        die $object->error, "\n"; +    } +    my $password = $object->get (@trace); +    $object->destroy (@trace); + +=head1 DESCRIPTION + +Wallet::Object::Password is an extension of Wallet::Object::File, +acting as a representation of simple file objects in the wallet.  The +difference between the two is that if there is no data stored in a +password object when a user tries to get it for the first time, then a +random string suited for a password will be generated and put into the +object data. + +It implements the wallet object API and provides the necessary +glue to store a file on the wallet server, retrieve it later, and delete +it when the password object is deleted. + +To use this object, the configuration option specifying where on the +wallet server to store password objects must be set.  See +L<Wallet::Config> for details on this configuration parameter and +information about how to set wallet configuration. + +=head1 METHODS + +This object mostly inherits from Wallet::Object::File.  See the +documentation for that class for all generic methods.  Below are only +those methods that are overridden or behave specially for this +implementation. + +=over 4 + +=item get(PRINCIPAL, HOSTNAME [, DATETIME]) + +Retrieves the current contents of the file object or undef on error. +store() must be called before get() will be successful.  The caller should +call error() to get the error message if get() returns undef.  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 + +=head1 FILES + +=over 4 + +=item PWD_FILE_BUCKET/<hash>/<file> + +Password files are stored on the wallet server under the directory +PWD_FILE_BUCKET as set in the wallet configuration.  <hash> is the +first two characters of the hex-encoded MD5 hash of the wallet password +object name, used to not put too many files in the same directory. +<file> is the name of the password object with all characters other +than alphanumerics, underscores, and dashes replaced by C<%> and the +hex code of the character. + +=back + +=head1 LIMITATIONS + +The wallet implementation itself can handle arbitrary password object +names. However, due to limitations in the B<remctld> server usually +used to run B<wallet-backend>, password object names containing nul +characters (ASCII 0) may not be permitted.  The file system used for +storing file objects may impose a length limitation on the +password object name. + +=head1 SEE ALSO + +remctld(8), Wallet::Config(3), Wallet::Object::File(3), +wallet-backend(8) + +This module is part of the wallet system.  The current version is +available from L<http://www.eyrie.org/~eagle/software/wallet/>. + +=head1 AUTHOR + +Jon Robertson <jonrober@stanford.edu> + +=cut diff --git a/perl/lib/Wallet/Object/WAKeyring.pm b/perl/lib/Wallet/Object/WAKeyring.pm index 3e80300..3c99785 100644 --- a/perl/lib/Wallet/Object/WAKeyring.pm +++ b/perl/lib/Wallet/Object/WAKeyring.pm @@ -1,6 +1,7 @@ -# Wallet::Object::WAKeyring -- WebAuth keyring object implementation. +# Wallet::Object::WAKeyring -- WebAuth keyring object implementation  #  # Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org>  # Copyright 2012, 2013, 2014  #     The Board of Trustees of the Leland Stanford Junior University  # @@ -11,24 +12,19 @@  ##############################################################################  package Wallet::Object::WAKeyring; -require 5.006; +use 5.008;  use strict;  use warnings; -use vars qw(@ISA $VERSION);  use Digest::MD5 qw(md5_hex);  use Fcntl qw(LOCK_EX); -use Wallet::Config (); +use Wallet::Config;  use Wallet::Object::Base;  use WebAuth 3.06 qw(WA_KEY_AES WA_AES_128); -@ISA = qw(Wallet::Object::Base); - -# 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.01'; +our @ISA     = qw(Wallet::Object::Base); +our $VERSION = '1.03';  ##############################################################################  # File naming | 
