diff options
| author | Bill MacAllister <whm@dropbox.com> | 2016-01-05 08:18:37 +0000 | 
|---|---|---|
| committer | Bill MacAllister <whm@dropbox.com> | 2016-01-05 08:18:37 +0000 | 
| commit | 9e1b210844faaa451cee3b9c0afa738120062e79 (patch) | |
| tree | 04dc0116e6db1af234359b2f92bc6c280eb7b2ff /perl | |
| parent | 2a03ce35be9b900cc0fd5f305dec54ebcf3fed5a (diff) | |
| parent | 802e47e8d84530d191817b2d86978a0b09803186 (diff) | |
Merge branch 'master' into ad-keytabs
Conflicts:
	NEWS
Diffstat (limited to 'perl')
| -rw-r--r-- | perl/lib/Wallet/ACL/External.pm | 198 | ||||
| -rw-r--r-- | perl/lib/Wallet/Config.pm | 35 | ||||
| -rw-r--r-- | perl/lib/Wallet/Kadmin/MIT.pm | 5 | ||||
| -rw-r--r-- | perl/lib/Wallet/Object/Duo.pm | 34 | ||||
| -rwxr-xr-x | perl/t/data/acl-command | 43 | ||||
| -rwxr-xr-x | perl/t/object/keytab.t | 26 | ||||
| -rw-r--r-- | perl/t/object/password.t | 1 | ||||
| -rwxr-xr-x | perl/t/verifier/external.t | 32 | 
8 files changed, 358 insertions, 16 deletions
| diff --git a/perl/lib/Wallet/ACL/External.pm b/perl/lib/Wallet/ACL/External.pm new file mode 100644 index 0000000..77c2499 --- /dev/null +++ b/perl/lib/Wallet/ACL/External.pm @@ -0,0 +1,198 @@ +# Wallet::ACL::External -- Wallet external ACL verifier +# +# Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# +# See LICENSE for licensing terms. + +############################################################################## +# Modules and declarations +############################################################################## + +package Wallet::ACL::External; +require 5.008; + +use strict; +use warnings; +use vars qw(@ISA $VERSION); + +use POSIX qw(_exit); +use Wallet::ACL::Base; +use Wallet::Config; + +@ISA = qw(Wallet::ACL::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'; + +############################################################################## +# Interface +############################################################################## + +# Creates a new persistent verifier.  This just checks if the configuration +# is in place. +sub new { +    my $type = shift; +    unless ($Wallet::Config::EXTERNAL_COMMAND) { +        die "external ACL support not configured\n"; +    } +    my $self = {}; +    bless ($self, $type); +    return $self; +} + +# The most trivial ACL verifier.  Returns true if the provided principal +# matches the ACL. +sub check { +    my ($self, $principal, $acl) = @_; +    unless ($principal) { +        $self->error ('no principal specified'); +        return; +    } +    my @args = split (' ', $acl); +    unshift @args, $principal; +    my $pid = open (EXTERNAL, '-|'); +    if (not defined $pid) { +        $self->error ("cannot fork: $!"); +        return; +    } elsif ($pid == 0) { +        unless (open (STDERR, '>&STDOUT')) { +            warn "wallet: cannot dup stdout: $!\n"; +            _exit(1); +        } +        unless (exec ($Wallet::Config::EXTERNAL_COMMAND, @args)) { +            warn "wallet: cannot run $Wallet::Config::EXTERNAL_COMMAND: $!\n"; +            _exit(1); +        } +    } +    local $_; +    my @output = <EXTERNAL>; +    close EXTERNAL; +    if ($? == 0) { +        return 1; +    } else { +        if (@output) { +            $self->error ($output[0]); +            return; +        } else { +            return 0; +        } +    } +} + +1; +__END__ + +############################################################################## +# Documentation +############################################################################## + +=for stopwords +ACL Allbery verifier remctl + +=head1 NAME + +Wallet::ACL::External - Wallet ACL verifier using an external command + +=head1 SYNOPSIS + +    my $verifier = Wallet::ACL::External->new; +    my $status = $verifier->check ($principal, $acl); +    if (not defined $status) { +        die "Something failed: ", $verifier->error, "\n"; +    } elsif ($status) { +        print "Access granted\n"; +    } else { +        print "Access denied\n"; +    } + +=head1 DESCRIPTION + +Wallet::ACL::External runs an external command to determine whether access is +granted.  The command configured via $EXTERNAL_COMMAND in L<Wallet::Config> +will be run.  The first argument to the command will be the principal +requesting access.  The identifier of the ACL will be split on whitespace and +passed in as the remaining arguments to this command. + +No other arguments are passed to the command, but the command will have access +to all of the remctl environment variables seen by the wallet server (such as +REMOTE_USER).  For a full list of environment variables, see +L<remctld(8)/ENVIRONMENT>. + +The external command should exit with a non-zero status but no output to +indicate a normal failure to satisfy the ACL.  Any output will be treated as +an error. + +=head1 METHODS + +=over 4 + +=item new() + +Creates a new ACL verifier.  For this verifier, this just confirms that +the wallet configuration sets an external command. + +=item check(PRINCIPAL, ACL) + +Returns true if the external command returns success when run with that +PRINCIPAL and ACL.  ACL will be split on whitespace and passed as multiple +arguments.  So, for example, the ACL C<external mdbset shell> will, when +triggered by a request from rra@EXAMPLE.COM, result in the command: + +    $Wallet::Config::EXTERNAL_COMMAND rra@EXAMPLE.COM mdbset shell + +=item error() + +Returns the error if check() returned undef. + +=back + +=head1 DIAGNOSTICS + +The new() method may fail with one of the following exceptions: + +=over 4 + +=item external ACL support not configured + +The required configuration parameters were not set.  See L<Wallet::Config> +for the required configuration parameters and how to set them. + +=back + +Verifying an external ACL may fail with the following errors (returned by +the error() method): + +=over 4 + +=item cannot fork: %s + +The attempt to fork in order to execute the external ACL verifier +command failed, probably due to a lack of system resources. + +=item no principal specified + +The PRINCIPAL parameter to check() was undefined or the empty string. + +=back + +In addition, if the external command fails and produces some output, +that will be considered a failure and the first line of its output will +be returned as the error message.  The external command should exit +with a non-zero status but no error to indicate a normal failure. + +=head1 SEE ALSO + +remctld(8), Wallet::ACL(3), Wallet::ACL::Base(3), Wallet::Config(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 + +Russ Allbery <eagle@eyrie.org> + +=cut diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm index b3e1931..98b5dc9 100644 --- a/perl/lib/Wallet/Config.pm +++ b/perl/lib/Wallet/Config.pm @@ -1,7 +1,8 @@  # Wallet::Config -- Configuration handling for the wallet server.  #  # Written by Russ Allbery <eagle@eyrie.org> -# Copyright 2007, 2008, 2010, 2013, 2014 +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# Copyright 2007, 2008, 2010, 2013, 2014, 2015  #     The Board of Trustees of the Leland Stanford Junior University  #  # See LICENSE for licensing terms. @@ -16,7 +17,7 @@ use vars qw($PATH $VERSION);  # This version should be increased on any code change to this module.  Always  # use two digits for the minor version with a leading zero if necessary so  # that it will sort properly. -$VERSION = '0.05'; +$VERSION = '0.06';  # Path to the config file to load.  $PATH = $ENV{WALLET_CONFIG} || '/etc/wallet/wallet.conf'; @@ -540,6 +541,36 @@ our $WAKEYRING_PURGE_INTERVAL = 60 * 60 * 24 * 90;  =back +=head1 EXTERNAL ACL CONFIGURATION + +This configuration variable is only needed if you intend to use the +C<external> ACL type (the Wallet::ACL::External class).  This ACL type +runs an external command to determine if access is granted. + +=over 4 + +=item EXTERNAL_COMMAND + +Path to the command to run to determine whether access is granted.  The +first argument to the command will be the principal requesting access. +The identifier of the ACL will be split on whitespace and passed in as the +remaining arguments to this command. + +No other arguments are passed to the command, but the command will have +access to all of the remctl environment variables seen by the wallet +server (such as REMOTE_USER).  For a full list of environment variables, +see L<remctld(8)/ENVIRONMENT>. + +The external command should exit with a non-zero status but no output to +indicate a normal failure to satisfy the ACL.  Any output will be treated +as an error. + +=cut + +our $EXTERNAL_COMMAND; + +=back +  =head1 LDAP ACL CONFIGURATION  These configuration variables are only needed if you intend to use the diff --git a/perl/lib/Wallet/Kadmin/MIT.pm b/perl/lib/Wallet/Kadmin/MIT.pm index ac45265..c5dea23 100644 --- a/perl/lib/Wallet/Kadmin/MIT.pm +++ b/perl/lib/Wallet/Kadmin/MIT.pm @@ -18,6 +18,7 @@ use strict;  use warnings;  use vars qw(@ISA $VERSION); +use POSIX qw(_exit);  use Wallet::Config ();  use Wallet::Kadmin (); @@ -65,11 +66,11 @@ sub kadmin {          $self->{fork_callback} () if $self->{fork_callback};          unless (open (STDERR, '>&STDOUT')) {              warn "wallet: cannot dup stdout: $!\n"; -            exit 1; +            _exit(1);          }          unless (exec ($Wallet::Config::KEYTAB_KADMIN, @args)) {              warn "wallet: cannot run $Wallet::Config::KEYTAB_KADMIN: $!\n"; -            exit 1; +            _exit(1);          }      }      local $_; diff --git a/perl/lib/Wallet/Object/Duo.pm b/perl/lib/Wallet/Object/Duo.pm index d0901de..378c123 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. @@ -18,8 +19,6 @@ 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::Base; @@ -159,8 +158,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, @@ -194,8 +205,20 @@ sub create {          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, @@ -204,7 +227,6 @@ sub create {      );      # Create the object in Duo. -    require Net::Duo::Admin::Integration;      my $duo_type = $DUO_TYPES{$type}{integration};      my %data = (          name  => "$name ($duo_type)", diff --git a/perl/t/data/acl-command b/perl/t/data/acl-command new file mode 100755 index 0000000..e368118 --- /dev/null +++ b/perl/t/data/acl-command @@ -0,0 +1,43 @@ +#!/bin/sh +# +# An external ACL implementation.  Checks that the first argument is +# eagle@eyrie.org, the second argument is "test", and then returns success, +# failure, or reports an error based on whether the second argument is +# success, failure, or error. +# +# Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# +# See LICENSE for licensing terms. + +set -e + +# Check the initial principal argument. +if [ "$1" != 'eagle@eyrie.org' ]; then +    echo 'incorrect principal' >&2 +    exit 1 +fi + +# Check that the second argument is test. +if [ "$2" != 'test' ]; then +    echo 'incorrect second argument' >&2 +    exit 1 +fi + +# Process the third argument. +case $3 in +    success) +        exit 0 +        ;; +    failure) +        exit 1 +        ;; +    error) +        echo 'some error' >&2 +        exit 1 +        ;; +    *) +        echo 'unknown third argument' >&2 +        exit 1 +        ;; +esac diff --git a/perl/t/object/keytab.t b/perl/t/object/keytab.t index 69db438..111b7d0 100755 --- a/perl/t/object/keytab.t +++ b/perl/t/object/keytab.t @@ -12,7 +12,7 @@ use strict;  use warnings;  use POSIX qw(strftime); -use Test::More tests => 141; +use Test::More tests => 142;  BEGIN { $Wallet::Config::KEYTAB_TMP = '.' } @@ -25,15 +25,28 @@ use Wallet::Object::Keytab;  use lib 't/lib';  use Util; -# Mapping of klist -ke encryption type names to the strings that Kerberos uses -# internally.  It's very annoying to have to maintain this, and it probably -# breaks with Heimdal. +# Mapping of klist -ke output from old MIT Kerberos implementations to to the +# strings that Kerberos uses internally.  It's very annoying to have to +# maintain this, and it probably breaks with Heimdal. +# +# Newer versions of MIT Kerberos just print out the canonical enctype names +# and don't need this logic, but the current test requires that they still +# have entries.  That's why the second set where the key and value are the +# same.  my %enctype =      ('triple des cbc mode with hmac/sha1'      => 'des3-cbc-sha1',       'des cbc mode with crc-32'                => 'des-cbc-crc',       'des cbc mode with rsa-md5'               => 'des-cbc-md5', +     'aes-128 cts mode with 96-bit sha-1 hmac' => 'aes128-cts-hmac-sha1-96',       'aes-256 cts mode with 96-bit sha-1 hmac' => 'aes256-cts-hmac-sha1-96', -     'arcfour with hmac/md5'                   => 'rc4-hmac'); +     'arcfour with hmac/md5'                   => 'rc4-hmac', + +     'des3-cbc-sha1'                           => 'des3-cbc-sha1', +     'des-cbc-crc'                             => 'des-cbc-crc', +     'des-cbc-md5'                             => 'des-cbc-md5', +     'aes128-cts-hmac-sha1-96'                 => 'aes128-cts-hmac-sha1-96', +     'aes256-cts-hmac-sha1-96'                 => 'aes256-cts-hmac-sha1-96', +     'rc4-hmac'                                => 'rc4-hmac');  # Some global defaults to use.  my $user = 'admin@EXAMPLE.COM'; @@ -159,7 +172,7 @@ my $date = strftime ('%Y-%m-%d %H:%M:%S', localtime $trace[2]);  # Basic keytab creation and manipulation tests.  SKIP: { -    skip 'no keytab configuration', 52 unless -f 't/data/test.keytab'; +    skip 'no keytab configuration', 53 unless -f 't/data/test.keytab';      # Set up our configuration.      $Wallet::Config::KEYTAB_FILE      = 't/data/test.keytab'; @@ -296,6 +309,7 @@ EOO                                          @trace)        };      ok (defined ($object), 'Creating good principal succeeds'); +    is ($@, '', ' with no error');      ok (created ('wallet/one'), ' and the principal was created');    SKIP: {          skip 'no kadmin program test for Heimdal', 2 diff --git a/perl/t/object/password.t b/perl/t/object/password.t index 4fe6b50..306d82b 100644 --- a/perl/t/object/password.t +++ b/perl/t/object/password.t @@ -120,5 +120,6 @@ like ($pwd, qr{^.{$Wallet::Config::PWD_LENGTH_MIN}$},  # Clean up.  $admin->destroy;  END { +    system ('rm -r test-files') == 0 or die "cannot remove test-files\n";      unlink ('wallet-db');  } diff --git a/perl/t/verifier/external.t b/perl/t/verifier/external.t new file mode 100755 index 0000000..3e7e776 --- /dev/null +++ b/perl/t/verifier/external.t @@ -0,0 +1,32 @@ +#!/usr/bin/perl +# +# Tests for the external wallet ACL verifier. +# +# Written by Russ Allbery <eagle@eyrie.org> +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# +# See LICENSE for licensing terms. + +use strict; +use warnings; + +use Test::More tests => 9; + +use Wallet::ACL::External; +use Wallet::Config; + +# Configure the external ACL verifier. +$Wallet::Config::EXTERNAL_COMMAND = 't/data/acl-command'; + +# Check a few verifications. +my $verifier = Wallet::ACL::External->new; +ok (defined $verifier, 'Wallet::ACL::External creation'); +ok ($verifier->isa ('Wallet::ACL::External'), ' and class verification'); +is ($verifier->check ('eagle@eyrie.org', 'test success'), 1, 'Success'); +is ($verifier->check ('eagle@eyrie.org', 'test failure'), 0, 'Failure'); +is ($verifier->error, undef, 'No error set'); +is ($verifier->check ('eagle@eyrie.org', 'test error'), undef, 'Error'); +is ($verifier->error, 'some error', ' and right error'); +is ($verifier->check (undef, 'eagle@eyrie.org'), undef, +    'Undefined principal'); +is ($verifier->error, 'no principal specified', ' and right error'); | 
