diff options
Diffstat (limited to 'perl/lib/Wallet/ACL/External.pm')
-rw-r--r-- | perl/lib/Wallet/ACL/External.pm | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/perl/lib/Wallet/ACL/External.pm b/perl/lib/Wallet/ACL/External.pm new file mode 100644 index 0000000..caed80e --- /dev/null +++ b/perl/lib/Wallet/ACL/External.pm @@ -0,0 +1,192 @@ +# Wallet::ACL::External -- Wallet external ACL verifier +# +# Copyright 2016 Russ Allbery <eagle@eyrie.org> +# +# See LICENSE for licensing terms. + +############################################################################## +# Modules and declarations +############################################################################## + +package Wallet::ACL::External; + +use 5.008; +use strict; +use warnings; + +use POSIX qw(_exit); +use Wallet::ACL::Base; +use Wallet::Config; + +our @ISA = qw(Wallet::ACL::Base); +our $VERSION = '1.03'; + +############################################################################## +# 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, $type, $name) = @_; + unless ($principal) { + $self->error ('no principal specified'); + return; + } + my @args = ($principal, $type, $name, $acl); + 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, TYPE, NAME) + +Returns true if the external command returns success when run with that +PRINCIPAL, object TYPE and NAME, and ACL. So, for example, the ACL C<external +mdbset shell> will, when triggered by a request from rra@EXAMPLE.COM for the +object C<file password>, result in the command: + + $Wallet::Config::EXTERNAL_COMMAND rra@EXAMPLE.COM file password \ + '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 |