Commit 9e1b2108 authored by Bill MacAllister's avatar Bill MacAllister
Browse files

Merge branch 'master' into ad-keytabs

Conflicts:
	NEWS
parents 2a03ce35 802e47e8
sudo: required
dist: trusty
language: c
compiler:
- gcc
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y libauthen-sasl-perl libcrypt-generatepassword-perl libdatetime-perl libdatetime-format-sqlite-perl libdbd-sqlite3-perl libdbi-perl libdbix-class-perl libheimdal-kadm5-perl libjson-perl libkrb5-dev libnet-dns-perl libnet-ldap-perl libnet-remctl-perl libperl6-slurp-perl libremctl-dev libsql-translator-perl libtest-minimumversion-perl libtest-pod-perl libtest-strict-perl libtimedate-perl libwebauth-perl perl sqlite3
env: AUTHOR_TESTING=1
script: ./autogen && ./configure && make warnings && make check
branches:
only:
- master
......@@ -2,10 +2,33 @@
wallet 1.3 (unreleased)
A new ACL type, nested (Wallet::ACL::Nested), is now supported. The
identifier of this ACL names another ACL, and access is granted if
that ACL would grant access. This lets one combine multiple other
ACLs and apply the union to an object. To enable this ACL type for an
existing wallet database, use wallet-admin to register the new
verifier.
A new ACL type, external (Wallet::ACL::External), is now supported.
This ACL runs an external command to check if access is allowed, and
passes the principal and the ACL identifier to that command. To
enable this ACL type for an existing wallet database, use wallet-admin
to register the new verifier.
A new variation on the ldap-attr ACL type, ldap-attr-root
(Wallet::ACL::LDAP::Attribute::Root), is now supported. This is
similar to netdb-root (compared to netdb): the authenticated principal
must end in /root, and the LDAP entry checked will be for the same
principal without the /root component. This is useful for limiting
access to certain privileged objects to Kerberos root instances. To
enable this ACL type for an existing wallet database, use wallet-admin
to register the new verifier.
A new object type, password (Wallet::Object::Password), is now
supported. This is a subclass of the file object that will randomly
generate content for the object if you do a get before storing any
content inside it.
content inside it. To enable this object type for an existing
database, use wallet-admin to register the new object.
Add a new command to wallet-backend, update. This will update the
contents of an object before running a get on it, and is only valid
......@@ -17,7 +40,8 @@ wallet 1.3 (unreleased)
warrants.
Add an acl replace command, to change all objects owned by one ACL to
be owned by another.
be owned by another. This currently only handles owner, not any of
the more specific ACLs.
All ACL operations now refer to the ACL by name rather than ID.
......@@ -25,11 +49,20 @@ wallet 1.3 (unreleased)
help for the existing unused report that implied it showed unstored as
well as unused.
Add reports that list all object types (types) and all ACL schemes
(schemes) currently registered in the wallet database.
Add a report of all ACLs that nest a given ACL. This requires some
additional local configuration (and probably some code). See
Wallet::Config for more information.
Took contributions from Commerzbank AG to improve wallet history. Add
a command to dump all object history for searching on to
wallet-report, and add a new script for more detailed object history
operations to the contrib directory.
Displays of ACLs and ACL entries are now sorted correctly.
Initial support for using Active Directory as the KDC for keytab
creation. The interface to Active Directory uses a combination of
direct LDAP queries and the msktutil utility. This version does
......
......@@ -109,7 +109,10 @@ REQUIREMENTS
WebAuth Perl module from WebAuth 4.4.0 or later.
The Duo integration object support in the wallet server requires the
Net::Duo Perl module.
Net::Duo, JSON, and Perl6::Slurp Perl modules.
The password object support in the wallet server requires the
Crypt::GeneratePassword Perl module.
To support the LDAP attribute ACL verifier, the Authen::SASL and
Net::LDAP Perl modules must be installed on the server. This verifier
......
This diff is collapsed.
......@@ -12,7 +12,7 @@
# Modules and declarations
##############################################################################
use 5.010;
use 5.008;
use autodie;
use strict;
use warnings;
......@@ -174,7 +174,7 @@ if ($mail) {
##############################################################################
=for stopwords
ACL NetDB SQL hostname lookup swhois whois Allbery
ACL NetDB SQL hostname lookup swhois whois Allbery -dryrun
=head1 NAME
......
......@@ -13,7 +13,7 @@ Introduction
Syntax
An ACL entry in the wallet consists of two pieces of data, a <scheme>
and an <instance>. <scheme> is one or more characters in the set
and an <identifier>. <scheme> is one or more characters in the set
[a-z0-9-] that identifies the ACL backend to use when interpreting
this ACL. <identifier> is zero or more characters including all
printable ASCII characters except whitespace. Only the implementation
......@@ -50,11 +50,35 @@ Semantics
ACL Schemes
external
The <identifier> is arguments to an external command. Access is
granted if the external command returns success. The standard remctl
environment variables are exposed to the external command.
krb5
The <identifier> is a fully-qualified Kerberos principal. Access is
granted if the principal of the client matches <identifier>.
ldap-attr
<identifier> is an an attribute followed by an equal sign and a value.
If the LDAP entry corresponding to the given principal contains the
attribute and value specified by <identifier>, access is granted.
ldap-attr-root
This is almost identical to netdb except that the user must be in the
form of a root instance (<user>/root) and the "/root" portion is
stripped before checking the NetDB roles.
nested
<identifier> is the name of another ACL, and access is granted if it
is granted by that ACL. This can be used to organize multiple ACLs
into a group and apply their union to an object.
netdb
<identifier> is the name of a system. Access is granted if the user
......@@ -67,13 +91,6 @@ ACL Schemes
form of a root instance (<user>/root) and the "/root" portion is
stripped before checking the NetDB roles.
ldap-entitlement
(Not yet implemented.) <identifier> is an entitlement. If the
entitlement attribute of the LDAP entry corresponding to the given
principal contains the entitlement specified in <identifier>, access
is granted.
pts
(Not yet implemented.) <identifier> is the name of an AFS PTS group.
......@@ -82,6 +99,7 @@ ACL Schemes
License
Copyright 2016 Russ Allbery <eagle@eyrie.org>
Copyright 2006, 2007, 2008, 2013
The Board of Trustees of the Leland Stanford Junior University
......
# 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
# 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
......
......@@ -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 $_;
......
# 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;
# Construct the Net::Duo::Admin object.
# 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.
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";
}
# Construct the Net::Duo::Admin object.
# 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.
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)",
......
#!/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
......@@ -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
......
......@@ -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');
}