Commit f61bff40 authored by Bill MacAllister's avatar Bill MacAllister
Browse files

Merge branch 'master' into ad-keytabs

Conflicts:
	NEWS
parents 0eb853eb 4a777845
User-Visible wallet Changes
wallet 1.3 (2015-11-27)
This version implements Active Directory as the store for keytabs.
The interface to Active Directory uses a combination of direct
LDAP queries and the msktutil utility. This version does not
support the wallet unchanging flag. Unchanging requires that a
keytab be retrieved without changing the password/kvno which is
wallet 1.3 (unreleased)
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.
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
for objects that can automatically get new content, such as keytab and
password objects. A keytab will get a new kvno regardless of the
unchanging flag if called with update. In a future release get will
be changed to never update a keytab, and the unchanging flag will be
ignored. Please start moving to use get or update as the situation
warrants.
Add an acl replace command, to change all objects owned by one ACL to
be owned by another.
All ACL operations now refer to the ACL by name rather than ID.
Add a report for unstored objects to wallet-report, and cleaned up the
help for the existing unused report that implied it showed unstored as
well as unused.
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.
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
not support the wallet unchanging flag. Unchanging requires that
a keytab be retrieved without changing the password/kvno which is
not supported by msktutil.
wallet 1.2 (2014-12-08)
......
......@@ -227,6 +227,16 @@ renamed. <id> may be either the current name or the numeric ID. <name>
must not be all-numeric. To rename an ACL, the current user must be
authorized by the C<ADMIN> ACL.
=item acl replace <id> <new-id>
Find any objects owned by <id>, and then change their ownership to
<new_id> instead. <new-id> should already exist, and may already have
some objects owned by it. <id> is not deleted afterwards, though in
most cases that is probably your next step. The C<ADMIN> ACL may not be
replaced from. <id> and <new-id> may be either the current name or the
numeric ID. To replace an ACL, the current user must be authorized by
the C<ADMIN> ACL.
=item acl show <id>
Display the name, numeric ID, and entries of the ACL <id>.
......@@ -375,6 +385,19 @@ If an object with type <type> and name <name> does not already exist when
this command is issued (as checked with the check interface), B<wallet>
will attempt to automatically create it (using autocreate).
=item update <type> <name>
Prints to standard output the data associated with the object identified
by <type> and <name>, or stores it in a file if the B<-f> option was
given. This will generate new data in the object, and only works for
objects that support generating new data automatically, such as keytabs or
passwords. Types that do not support generating new data will fail and
direct you to use get instead.
If an object with type <type> and name <name> does not already exist when
this command is issued (as checked with the check interface), B<wallet>
will attempt to automatically create it (using autocreate).
=back
=head1 ATTRIBUTES
......
#!/usr/bin/perl -w
#
# -*- perl -*-
#
#--------------------------------------------------------------------------------------------------------------
# Program : wallet-history
# Function : Tool for listing and/or modifying Wallet's object history
# Author : Commerzbank AG
# History : 20.01.2014 - V0.1 - First version - maxcrc
# : 21.01.2014 - V0.2 - Fixed some minor bugs and code formatting - Gerhard Stahl
# Added some remarks for nessesary fixes
# : 29.01.2014 - V0.2 - Fixed issue related to variable comparision of undef values - maxcrc
# Reformatted code to use functions like debug, usage - maxcrc
# : 06.02.2014 - V0.3 - Added support for ACL history in addition to object history - maxcrc
# : 07.02.2014 - V0.4 - A lot of improvements, run with --help to see - maxcrc
# --acl and --obj argument names removed, please use --acls and --objs
# --from and --to argument names replaced with --start and --end
# Added new filtering args --from, --action, --acl, --by, --type and --name
# Added new --version action type
# Added new --columns action type
# : 08.02.2014 - V0.5 - Added support for ACL names via new ah_name column - maxcrc
#
#--------------------------------------------------------------------------------------------------------------
#
# Usage:
#
# type ./wallet-history with no arguments to get the full help text (or with --help).
#
# In general, use a command of the form:
#
# perl wallet-history.pl ... (t.b.d.)...
#
#--------------------------------------------------------------------------------------------------------------
# Version.
my $VERSION = "0.5";
=head1 Needed Modules
Here is the list of modules we need:
=cut
use strict;
use Getopt::Long;
use Wallet::Schema;
use DateTime;
use DateTime::Format::Strptime;
#use DBIx::ResultSet;
use DBI;
#use DateTime ();
#use Scalar::Util;
my $debug_on = 0;
=head1 Subroutines
=head2 Utility subroutines
=head3 usage - display a usage message
=cut
sub usage() {
print "wallet-history is tool for listing and/or modifying Wallet's history\n";
print "usage:\n";
print "wallet-history [mandatory action] [optional query type] [optional filtering arguments]\n";
print " where mandatory action is one of:\n";
print " --help - display this help information\n";
print " --version - display app version\n";
print " --columns - display column names (use with --objs or --acls)\n";
print " --list - list ACL or object history with/without from/to date filters\n";
print " --clear - clear ACL or object history with/without from/to date filters\n";
print " optional query type is one of:\n";
print " --acls - query is for ACL history (default is --objs)\n";
print " --objs - query is for object history (this is default)\n";
print " optional filtering data entries by (any [name] can be regular expression):\n";
print " --start [date/time] - starting date\n";
print " --end [date/time] - ending date\n";
print " --from [name] - computer name\n";
print " --action [name] - action\n";
print " --acl [name] - acl (with --acls only)\n";
print " --by [name] - by address (with --acls only)\n";
print " --type [name] - type (with --objs only)\n";
print " --name [name] - host name with --objs, acl name with --acls\n";
}
=head3 usage - prints debug messages
=cut
sub debug {
my $msg = @_;
return unless ( $debug_on == 1);
print $msg . "\n";
}
my $parser = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S',
on_error => 'croak',
);
my $opt_result = undef;
my $an_action_help = undef;
my $an_action_version = undef;
my $an_action_columns = undef;
my $an_action_list = undef;
my $an_action_clear = undef;
my $is_action_acls = 0;
my $is_action_objs = 1;
my $str_date_from = undef;
my $str_date_to = undef;
my $str_filter_computer_name = undef;
my $str_filter_action = undef;
my $str_filter_acl = undef;
my $str_filter_by = undef;
my $str_filter_type = undef;
my $str_filter_name = undef;
$opt_result = GetOptions (
"help" => \$an_action_help
, "version" => \$an_action_version
, "columns" => \$an_action_columns
, "list" => \$an_action_list
, "clear" => \$an_action_clear
, "acls" => \$is_action_acls
, "objs" => \$is_action_objs
, "start=s" => \$str_date_from
, "end=s" => \$str_date_to
, "from=s" => \$str_filter_computer_name
, "action=s" => \$str_filter_action
, "acl=s" => \$str_filter_acl
, "by=s" => \$str_filter_by
, "type=s" => \$str_filter_type
, "name=s" => \$str_filter_name
);
if( ( defined $an_action_help ) or ( defined $an_action_version ) ) {
if( defined $an_action_version ) {
print "wallet-history version is $VERSION\n";
}
if( defined $an_action_help ) {
usage;
}
exit 0; # FIN #
}
my $str_resultset_name = undef;
if( $is_action_acls ) {
$is_action_objs = 0;
$str_resultset_name = 'AclHistory';
} else {
$is_action_objs = 1;
$str_resultset_name = 'ObjectHistory';
}
if( defined $an_action_columns ) {
if( $is_action_acls ) {
print "\"ah_on\" \"ah_acl\" \"ah_by\" \"ah_action\" \"ah_from\"\n";
} else { if( $is_action_objs ) {
print "\"oh_on\" \"oh_by\" \"oh_type\" \"oh_name\" \"oh_action\" \"oh_from\"\n";
} else {
die "Critical internal error - unknown dataset type\n";
} }
exit 0; # FIN #
}
my $date_from = undef;
if ( defined $str_date_from ) {
$date_from = $parser->parse_datetime( $str_date_from );
debug "Using minimal(from) date $date_from\n";
}
my $date_to = undef;
if ( defined $str_date_to ) {
$date_to = $parser->parse_datetime( $str_date_to );
debug "Using maximal(to) date $date_to\n";
}
if ( $an_action_list || $an_action_clear ) {
my $schema = Wallet::Schema->connect;
my @data_entries;
eval {
my @data_entries_rs = $schema->resultset( $str_resultset_name )->search(); # ( $search_ref, $options_ref );
for my $entry_rs (@data_entries_rs) {
if( $is_action_acls ) {
# begin - working with ACLs history
my $is_filter_passed = 1;
my $str_date_on = "" . $entry_rs->ah_on;
my $date_on = $parser->parse_datetime( $str_date_on );
debug "date on as string ", $str_date_on, "\n";
debug "date on as date ", $date_on, "\n";
if ( ( defined $date_from ) || ( defined $date_to ) ) {
if ( ( defined $date_from ) && ( $date_from > $date_on ) ) {
$is_filter_passed = 0;
}
if ( ( defined $date_to ) && ( $date_to < $date_on ) ) {
$is_filter_passed = 0;
}
} # if ( ( defined $date_from ) || ( defined $date_to ) )
if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) ) {
my $str_text_to_test = "" . $entry_rs->ah_from;
if( not ( $str_text_to_test =~ /$str_filter_computer_name/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) )
if( $is_filter_passed != 0 && ( defined $str_filter_action ) ) {
my $str_text_to_test = "" . $entry_rs->ah_action;
if( not ( $str_text_to_test =~ /$str_filter_action/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_action ) )
if( $is_filter_passed != 0 && ( defined $str_filter_acl ) ) {
my $str_text_to_test = "" . $entry_rs->ah_acl;
if( not ( $str_text_to_test =~ /$str_filter_acl/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_acl ) )
if( $is_filter_passed != 0 && ( defined $str_filter_by ) ) {
my $str_text_to_test = "" . $entry_rs->ah_by;
if( not ( $str_text_to_test =~ /$str_filter_by/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_by ) )
if( $is_filter_passed != 0 && ( defined $str_filter_name ) ) {
my $str_text_to_test = "" . $entry_rs->ah_name;
if( not ( $str_text_to_test =~ /$str_filter_name/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_name ) )
if( $is_filter_passed != 0 ) {
push (@data_entries, [$entry_rs->ah_on, $entry_rs->ah_acl, $entry_rs->ah_by, $entry_rs->ah_action, $entry_rs->ah_from, $entry_rs->ah_name ]);
debug "Found: \"", $entry_rs->ah_on, "\" \"", $entry_rs->ah_acl, "\" \"", $entry_rs->ah_by, "\" \"", $entry_rs->ah_action, "\" \"", $entry_rs->ah_from, "\" \"", $entry_rs->ah_name, "\n";
} # if( $is_filter_passed != 0 )
debug "walked throgh dates \"", ref($date_from), "-", $date_from, "\" \"", ref($date_on), "-", $date_on, "\" \"", ref($date_to), "-", $date_to, "\" result is ", $is_filter_passed, "\n";
# end - working with ACLs history
} else { if( $is_action_objs ) {
# begin - working with objects history
my $is_filter_passed = 1;
my $str_date_on = "" . $entry_rs->oh_on;
my $date_on = $parser->parse_datetime( $str_date_on );
debug "date on as string ", $str_date_on, "\n";
debug "date on as date ", $date_on, "\n";
if ( ( defined $date_from ) || ( defined $date_to ) ) {
if ( ( defined $date_from ) && ( $date_from > $date_on ) ) {
$is_filter_passed = 0;
}
if ( ( defined $date_to ) && ( $date_to < $date_on ) ) {
$is_filter_passed = 0;
}
} # if ( ( defined $date_from ) || ( defined $date_to ) )
if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) ) {
my $str_text_to_test = "" . $entry_rs->oh_from;
if( not ( $str_text_to_test =~ /$str_filter_computer_name/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) )
if( $is_filter_passed != 0 && ( defined $str_filter_action ) ) {
my $str_text_to_test = "" . $entry_rs->oh_action;
if( not ( $str_text_to_test =~ /$str_filter_action/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_action ) )
if( $is_filter_passed != 0 && ( defined $str_filter_type ) ) {
my $str_text_to_test = "" . $entry_rs->oh_type;
if( not ( $str_text_to_test =~ /$str_filter_type/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_type ) )
if( $is_filter_passed != 0 && ( defined $str_filter_name ) ) {
my $str_text_to_test = "" . $entry_rs->oh_name;
if( not ( $str_text_to_test =~ /$str_filter_name/ ) ) {
$is_filter_passed = 0;
}
} # if( $is_filter_passed != 0 && ( defined $str_filter_name ) )
if( $is_filter_passed != 0 ) {
push (@data_entries, [$entry_rs->oh_on, $entry_rs->oh_by, $entry_rs->oh_type, $entry_rs->oh_name, $entry_rs->oh_action, $entry_rs->oh_from ]);
debug "Found: \"", $entry_rs->oh_on, "\" \"", $entry_rs->oh_by, "\" \"", $entry_rs->oh_type, "\" \"", $entry_rs->oh_name, "\" \"", $entry_rs->oh_action, "\" \"", $entry_rs->oh_from, "\"", "\n";
} # if( $is_filter_passed != 0 )
debug "walked throgh dates \"", ref($date_from), "-", $date_from, "\" \"", ref($date_on), "-", $date_on, "\" \"", ref($date_to), "-", $date_to, "\" result is ", $is_filter_passed, "\n";
# end - working with objects history
} else {
die "Critical internal error - unknown dataset type\n";
} }
} # for my $entry_rs (@data_entries_rs)
}; # eval
if ($@) {
print "cannot list data entries: $@";
exit -1;
}
if( $is_action_acls ) {
# begin - working with ACLs history
for my $group (@data_entries) {
if ( $an_action_clear ) {
my $is_deleted_ok = 0;
print "\"", join ("\" \"", @$group), "\" ... ";
my $sql_delete_error = undef;
eval {
my $str_ah_on = "" . $group->[0];
my $str_ah_acl = "" . $group->[1];
my $str_ah_by = "" . $group->[2];
my $str_ah_action = "" . $group->[3];
my $str_ah_from = "" . $group->[4];
my $str_ah_name = "" . $group->[5];
my $string = $str_ah_on; # '01234567890';
my $find = 'T'; # '0';
my $replace = ' '; # 'a';
my $pos = index($string, $find);
while ( $pos > -1 ) {
substr( $string, $pos, length( $find ), $replace );
$pos = index( $string, $find, $pos + length( $replace ));
}
$str_ah_on = $string;
debug "Searching \"$str_ah_on\" \"$str_ah_acl\" \"$str_ah_by\" \"$str_ah_action\" \"$str_ah_from\" \"$str_ah_name\" ... \n";
#my @data_entries_rs =
# $schema->resultset('AclHistory')->search( [
# { ah_on => $str_ah_on }
# , { ah_acl => $str_ah_acl }
# , { ah_by => $str_ah_by }
# , { ah_action => $str_ah_action }
# , { ah_from => $str_ah_from }
# , { ah_name => $str_ah_name }
# ] ); # -> delete;
#my @data_entries_rs =
# $schema->resultset('AclHistory')->search( [
# { ah_on => { '=' , $str_ah_on } }
# , { ah_acl => { '=' , $str_ah_acl } }
# , { ah_by => { '=' , $str_ah_by } }
# , { ah_action => { '=' , $str_ah_action } }
# , { ah_from => { '=' , $str_ah_from } }
# , { ah_name => { '=' , $str_ah_name } }
# ] ); # -> delete;
my $str_sql_del = "DELETE FROM acl_history WHERE ah_on = TO_DATE(\'" . $str_ah_on . "\', 'YYYY-MM-DD HH24:MI:SS') AND ah_acl = \'" . $str_ah_acl . "\' AND ah_by = \'" . $str_ah_by . "\' AND ah_action = \'" . $str_ah_action . "\' AND ah_from = \'" . $str_ah_from . "\' AND ah_name = \'" . $str_ah_name . "\'";
#$schema->storage->debug(1);
my @ret = $schema->storage->dbh_do(
sub {
my ($storage, $dbh, @args) = @_;
debug "Attempting to ", $str_sql_del, "\n";
debug "dbh is ", $dbh, "\n";
my $sth = $dbh->prepare( $str_sql_del );
#$sth->{PrintError} = 1;
#$sth->{RaiseError} = 1;
debug "sth is ", $sth, "\n";
my $ret = $sth->execute(); # or die "Can't execute SQL statement: $DBI::errstr\n";
debug "ret is ", $ret, "\n";
debug "error string is", $sth->errstr(), "\n";
if ( $ret == 0 ) {
$sql_delete_error = "Internal error";
} else {
$is_deleted_ok = 1;
}
},
$str_sql_del
);
#$is_deleted_ok = 1;
#for my $entry_rs (@data_entries_rs) {
# print "Will delete: \"", $entry_rs->ah_on, "\" \"", $entry_rs->ah_acl, "\" \"", $entry_rs->ah_by, "\" \"", $entry_rs->ah_action, "\" \"", $entry_rs->ah_from, "\"", "\n";
#} # for my $entry_rs (@data_entries_rs)
}; # eval
if ( $is_deleted_ok ) {
print "Deleted\n";
} else {
print "Error ", $sql_delete_error, "\n";
}
} else { # else from if ( $an_action_clear )
print "\"", join ("\" \"", @$group), "\"\n";
} # else from if ( $an_action_clear )
} # for my $group (@data_entries)
# end - working with ACLs history
} else { if( $is_action_objs ) {
# begin - working with objects history
for my $group (@data_entries) {
if ( $an_action_clear ) {
my $is_deleted_ok = 0;
print "\"", join ("\" \"", @$group), "\" ... ";
my $sql_delete_error = undef;
eval {
my $str_oh_on = "" . $group->[0];
my $str_oh_by = "" . $group->[1];
my $str_oh_type = "" . $group->[2];
my $str_oh_name = "" . $group->[3];
my $str_oh_action = "" . $group->[4];
my $str_oh_from = "" . $group->[5];
my $string = $str_oh_on; # '01234567890';
my $find = 'T'; # '0';
my $replace = ' '; # 'a';
my $pos = index($string, $find);
while ( $pos > -1 ) {
substr( $string, $pos, length( $find ), $replace );
$pos = index( $string, $find, $pos + length( $replace ));
}
$str_oh_on = $string;
debug "Searching \"$str_oh_on\" \"$str_oh_by\" \"$str_oh_type\" \"$str_oh_name\" \"$str_oh_action\" \"$str_oh_from\" ... \n";
#my @data_entries_rs =
# $schema->resultset('ObjectHistory')->search( [
# { oh_on => $str_oh_on }
# , { oh_type => $str_oh_type }
# , { oh_name => $str_oh_name }
# , { oh_action => $str_oh_action }
# , { oh_from => $str_oh_from }
# ] ); # -> delete;
#my @data_entries_rs =
# $schema->resultset('ObjectHistory')->search( [
# { oh_on => { '=' , $str_oh_on } }
# , { oh_type => { '=' , $str_oh_type } }
# , { oh_name => { '=' , $str_oh_name } }
# , { oh_action => { '=' , $str_oh_action } }
# , { oh_from => { '=' , $str_oh_from } }
# ] ); # -> delete;
my $str_sql_del = "DELETE FROM object_history WHERE oh_on = TO_DATE(\'" . $str_oh_on . "\', 'YYYY-MM-DD HH24:MI:SS') AND oh_type = \'" . $str_oh_type . "\' AND oh_name = \'" . $str_oh_name . "\' AND oh_action = \'" . $str_oh_action . "\' AND oh_from = \'" . $str_oh_from . "\'";
#$schema->storage->debug(1);
my @ret = $schema->storage->dbh_do(
sub {
my ($storage, $dbh, @args) = @_;
debug "Attempting to ", $str_sql_del, "\n";
debug "dbh is ", $dbh, "\n";
my $sth = $dbh->prepare( $str_sql_del );
#$sth->{PrintError} = 1;
#$sth->{RaiseError} = 1;
debug "sth is ", $sth, "\n";
my $ret = $sth->execute(); # or die "Can't execute SQL statement: $DBI::errstr\n";
debug "ret is ", $ret, "\n";
debug "error string is", $sth->errstr(), "\n";
if ( $ret == 0 ) {
$sql_delete_error = "Internal error";
} else {
$is_deleted_ok = 1;
}
},
$str_sql_del
);
#$is_deleted_ok = 1;
#for my $entry_rs (@data_entries_rs) {
# print "Will delete: \"", $entry_rs->oh_on, "\" \"", $entry_rs->oh_type, "\" \"", $entry_rs->oh_name, "\" \"", $entry_rs->oh_action, "\" \"", $entry_rs->oh_from, "\"", "\n";
#} # for my $entry_rs (@data_entries_rs)
}; # eval
if ( $is_deleted_ok ) {
print "Deleted\n";
} else {
print "Error ", $sql_delete_error, "\n";
}
} else { # else from if ( $an_action_clear )
print "\"", join ("\" \"", @$group), "\"\n";
} # else from if ( $an_action_clear )
} # for my $group (@data_entries)
# end - working with objects history
} else {
die "Critical internal error - unknown dataset type\n";
} }
exit 0; # FIN #
} # if ( $an_action_list || $an_action_clear )
usage;
exit 0; # FIN #
......@@ -3,7 +3,7 @@
# wallet-contacts -- Report contact addresses for matching wallet objects.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2009
# Copyright 2009, 2015
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
......@@ -12,16 +12,32 @@
# Modules and declarations
##############################################################################
require 5.006;
use 5.010;
use autodie;
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Wallet::Admin ();
use Perl6::Slurp;
use Wallet::Report ();
# Used to cache lookups of e-mail addresses by identifiers.
our %EMAIL;
##############################################################################
# Mail sending
##############################################################################
# Given a message, mail it through sendmail.
sub mail {
my ($message) = @_;
open (MAIL, '| /usr/sbin/sendmail -t -oi -oem')
or die "$0: cannot fork sendmail: $!\n";
print MAIL $message;
close (MAIL);
}
##############################################################################
# whois lookups