aboutsummaryrefslogtreecommitdiff
path: root/perl
diff options
context:
space:
mode:
authorBill MacAllister <whm@dropbox.com>2016-01-05 08:18:37 +0000
committerBill MacAllister <whm@dropbox.com>2016-01-05 08:18:37 +0000
commit9e1b210844faaa451cee3b9c0afa738120062e79 (patch)
tree04dc0116e6db1af234359b2f92bc6c280eb7b2ff /perl
parent2a03ce35be9b900cc0fd5f305dec54ebcf3fed5a (diff)
parent802e47e8d84530d191817b2d86978a0b09803186 (diff)
Merge branch 'master' into ad-keytabs
Conflicts: NEWS
Diffstat (limited to 'perl')
-rw-r--r--perl/lib/Wallet/ACL/External.pm198
-rw-r--r--perl/lib/Wallet/Config.pm35
-rw-r--r--perl/lib/Wallet/Kadmin/MIT.pm5
-rw-r--r--perl/lib/Wallet/Object/Duo.pm34
-rwxr-xr-xperl/t/data/acl-command43
-rwxr-xr-xperl/t/object/keytab.t26
-rw-r--r--perl/t/object/password.t1
-rwxr-xr-xperl/t/verifier/external.t32
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');