diff options
| author | Russ Allbery <eagle@eyrie.org> | 2014-12-08 18:33:09 -0800 | 
|---|---|---|
| committer | Russ Allbery <eagle@eyrie.org> | 2014-12-08 18:33:09 -0800 | 
| commit | 535ab35781b30805962201874ffff16492ca265d (patch) | |
| tree | 433a3446b17227a87f29e064aa710a88f7f8a4ac /perl/lib/Wallet/Object | |
| parent | d5968989923768688b7bd2e4279a9db43a2b93df (diff) | |
| parent | 49f5b0d38e96d13b6cfb329b21599f9d6ff853d6 (diff) | |
Merge pull request #1 from jonrober/master
Duo and rename updates
Diffstat (limited to 'perl/lib/Wallet/Object')
| -rw-r--r-- | perl/lib/Wallet/Object/Base.pm | 2 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo.pm | 54 | ||||
| -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 | 56 | 
7 files changed, 900 insertions, 27 deletions
| diff --git a/perl/lib/Wallet/Object/Base.pm b/perl/lib/Wallet/Object/Base.pm index a6a78bf..bdd61fb 100644 --- a/perl/lib/Wallet/Object/Base.pm +++ b/perl/lib/Wallet/Object/Base.pm @@ -187,7 +187,7 @@ sub log_set {      }      my %fields = map { $_ => 1 }          qw(owner acl_get acl_store acl_show acl_destroy acl_flags expires -           comment flags type_data); +           comment flags type_data name);      unless ($fields{$field}) {          die "invalid history field $field";      } diff --git a/perl/lib/Wallet/Object/Duo.pm b/perl/lib/Wallet/Object/Duo.pm index 6edc4fa..d08294b 100644 --- a/perl/lib/Wallet/Object/Duo.pm +++ b/perl/lib/Wallet/Object/Duo.pm @@ -1,4 +1,4 @@ -# Wallet::Object::Duo -- Duo integration object implementation for the wallet. +# Wallet::Object::Duo -- Base Duo object implementation for the wallet  #  # Written by Russ Allbery <eagle@eyrie.org>  # Copyright 2014 @@ -29,7 +29,7 @@ use 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'; +$VERSION = '0.02';  ##############################################################################  # Core methods @@ -41,7 +41,9 @@ sub attr_show {      my $output = '';      my $key;      eval { -        my %search = (du_name => $self->{name}); +        my %search = (du_name => $self->{name}, +                      du_type => $self->{type}, +                     );          my $row = $self->{schema}->resultset ('Duo')->find (\%search);          $key = $row->get_column ('du_key');      }; @@ -84,7 +86,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) = @_; +    my ($class, $type, $name, $schema, $creator, $host, $time, $duo_type) = @_;      # We have to have a Duo integration key file set.      if (not $Wallet::Config::DUO_KEY_FILE) { @@ -104,10 +106,11 @@ sub create {      # Create the object in Duo.      require Net::Duo::Admin::Integration; +    $duo_type ||= $Wallet::Config::DUO_TYPE;      my %data = ( -        name  => "$name ($Wallet::Config::DUO_TYPE)", +        name  => "$name ($duo_type)",          notes => 'Managed by wallet', -        type  => $Wallet::Config::DUO_TYPE, +        type  => $duo_type,      );      my $integration = Net::Duo::Admin::Integration->create ($duo, \%data); @@ -121,6 +124,7 @@ sub create {      eval {          my %record = (              du_name => $name, +            du_type => $type,              du_key  => $integration->integration_key,          );          $self->{schema}->resultset ('Duo')->create (\%record); @@ -147,7 +151,9 @@ sub destroy {      my $schema = $self->{schema};      my $guard = $schema->txn_scope_guard;      eval { -        my %search = (du_name => $self->{name}); +        my %search = (du_name => $self->{name}, +                      du_type => $self->{type}, +                     );          my $row = $schema->resultset ('Duo')->find (\%search);          my $key = $row->get_column ('du_key');          my $int = Net::Duo::Admin::Integration->new ($self->{duo}, $key); @@ -178,7 +184,9 @@ sub get {      # Retrieve the integration from Duo.      my $key;      eval { -        my %search = (du_name => $self->{name}); +        my %search = (du_name => $self->{name}, +                      du_type => $self->{type}, +                     );          my $row = $self->{schema}->resultset ('Duo')->find (\%search);          $key = $row->get_column ('du_key');      }; @@ -194,10 +202,10 @@ sub get {      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"; +    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); @@ -234,12 +242,11 @@ 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. -Currently, only one configured integration type can be managed by the -wallet, and 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. +Usually you will want to use one of the subclasses of this module, which +override the output to give you a configuration fragment suited for a +specific application type.  However, you can always use this module for +generic integrations where you don't mind massaging the output into the +configuration for the application using Duo.  This object can be retrieved repeatedly without changing the secret key,  matching Duo's native behavior with integrations.  To change the keys of @@ -258,7 +265,7 @@ implementation.  =over 4 -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) +=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME, INTEGRATION_TYPE])  This is a class method and should be called on the Wallet::Object::Duo  class.  It creates a new object with the given TYPE and NAME (TYPE is @@ -272,9 +279,9 @@ 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.  The new integration's type is controlled by the DUO_TYPE -configuration variable, which defaults to C<unix>.  See L<Wallet::Config> -for more information. +will fail.  If an integration type isn't given, the new integration's type +is controlled by the DUO_TYPE configuration variable, which defaults to +C<unix>.  See L<Wallet::Config> for more information.  If create() fails, it throws an exception. @@ -314,9 +321,6 @@ isn't given, the current time is used.  =head1 LIMITATIONS  Only one Duo account is supported for a given wallet implementation. -Currently, only one Duo integration type is supported as well.  Further -development should expand the available integration types, possibly as -additional wallet object types.  =head1 SEE ALSO diff --git a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm new file mode 100644 index 0000000..757a61a --- /dev/null +++ b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm @@ -0,0 +1,202 @@ +# 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 + +=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 new file mode 100644 index 0000000..6f90ba1 --- /dev/null +++ b/perl/lib/Wallet/Object/Duo/PAM.pm @@ -0,0 +1,205 @@ +# 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 new file mode 100644 index 0000000..4e7ff04 --- /dev/null +++ b/perl/lib/Wallet/Object/Duo/RDP.pm @@ -0,0 +1,204 @@ +# 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 + +=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 new file mode 100644 index 0000000..2a29dec --- /dev/null +++ b/perl/lib/Wallet/Object/Duo/RadiusProxy.pm @@ -0,0 +1,204 @@ +# 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 + +=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 1ff1288..226e32c 100644 --- a/perl/lib/Wallet/Object/File.pm +++ b/perl/lib/Wallet/Object/File.pm @@ -18,6 +18,7 @@ use warnings;  use vars qw(@ISA $VERSION);  use Digest::MD5 qw(md5_hex); +use File::Copy qw(move);  use Wallet::Config ();  use Wallet::Object::Base; @@ -55,6 +56,59 @@ sub file_path {      return "$Wallet::Config::FILE_BUCKET/$hash/$name";  } +# Rename a file object.  This includes renaming both the object itself, and +# updating the file location for that object. +sub rename { +    my ($self, $new_name, $user, $host, $time) = @_; +    $time ||= time; + +    my $old_name = $self->{name}; +    my $type     = $self->{type}; +    my $schema   = $self->{schema}; +    my $old_path = $self->file_path; + +    eval { + +        # Find the current object record. +        my $guard = $schema->txn_scope_guard; +        my %search = (ob_type => $type, +                      ob_name => $old_name); +        my $object = $schema->resultset('Object')->find (\%search); +        die "cannot find ${type}:${old_name}\n" +            unless ($object and $object->ob_name eq $old_name); + +        # Update the object name but don't yet commit. +        $object->ob_name ($new_name); + +        # Update the file to the path for the new name, and die if we can't. +        # If the old path isn't there, then assume we haven't yet stored and +        # keep going. +        if ($old_path) { +            $self->{name} = $new_name; +            my $new_path = $self->file_path; +            move($old_path, $new_path) or die $!; +        } + +        $object->update; +        $guard->commit; +    }; +    if ($@) { +        $self->{name} = $old_name; +        $self->error ("cannot rename object $type $old_name: $!"); +        return; +    } + +    eval { +        $self->log_set ('name', $old_name, $new_name, $user, $host, $time); +    }; +    if ($@) { +        $self->error ("object $type $old_name was renamed but not logged: $!"); +        return 1; +    } + +    return 1; +} +  ##############################################################################  # Core methods  ############################################################################## @@ -145,7 +199,7 @@ API HOSTNAME DATETIME keytab remctld backend nul Allbery wallet-backend      my @name = qw(file mysql-lsdb)      my @trace = ($user, $host, time); -    my $object = Wallet::Object::Keytab->create (@name, $schema, @trace); +    my $object = Wallet::Object::File->create (@name, $schema, @trace);      unless ($object->store ("the-password\n")) {          die $object->error, "\n";      } | 
