<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">--- server/wallet-backend	2016-01-17 19:13:02.000000000 -0800
+++ /dev/null	2016-01-23 14:00:27.000000000 -0800
@@ -1,695 +0,0 @@
-#!/usr/bin/perl
-#
-# Wallet server for storing and retrieving secure data.
-
-use 5.008;
-use strict;
-use warnings;
-
-use Getopt::Long qw(GetOptions);
-use Sys::Syslog qw(openlog syslog);
-use Wallet::Server;
-
-# Set to zero to suppress syslog logging, which is used for testing and for
-# the -q option.  Set to a reference to a string to append messages to that
-# string instead.
-our $SYSLOG;
-$SYSLOG = 1 unless defined $SYSLOG;
-
-##############################################################################
-# Logging
-##############################################################################
-
-# Initialize logging.
-sub log_init {
-    if (ref $SYSLOG) {
-        $$SYSLOG = '';
-    } elsif ($SYSLOG) {
-        openlog ('wallet-backend', 'pid', 'auth');
-    }
-}
-
-# Get an identity string for the user suitable for including in log messages.
-sub identity {
-    my $identity = '';
-    if ($ENV{REMOTE_USER}) {
-        $identity = $ENV{REMOTE_USER};
-        my $host = $ENV{REMOTE_HOST} || $ENV{REMOTE_ADDR};
-        $identity .= " ($host)" if $host;
-    }
-    return $identity;
-}
-
-# Log an error message to both syslog and to stderr and exit with a non-zero
-# status.
-sub error {
-    my $message = join ('', @_);
-    if ($SYSLOG) {
-        my $identity = identity;
-        my $log;
-        if ($identity) {
-            $log = "error for $identity: $message";
-        } else {
-            $log = "error: $message";
-        }
-        $log =~ s/[^\x20-\x7e]/_/g;
-        if (ref $SYSLOG) {
-            $$SYSLOG .= "$log\n";
-        } else {
-            syslog ('err', "%s", $log);
-        }
-    }
-    die "$message\n";
-}
-
-# Log a wallet failure message for a given command to both syslog and to
-# stderr and exit with a non-zero status.  Takes the message and the command
-# that was being run.
-sub failure {
-    my ($message, @command) = @_;
-    if ($SYSLOG) {
-        my $log = "command @command from " . identity . " failed: $message";
-        $log =~ s/[^\x20-\x7e]/_/g;
-        if (ref $SYSLOG) {
-            $$SYSLOG .= "$log\n";
-        } else {
-            syslog ('err', "%s", $log);
-        }
-    }
-    die "$message\n";
-}
-
-# Log a wallet success message for a given command.
-sub success {
-    my (@command) = @_;
-    if ($SYSLOG) {
-        my $log = "command @command from " . identity . " succeeded";
-        $log =~ s/[^\x20-\x7e]/_/g;
-        if (ref $SYSLOG) {
-            $$SYSLOG .= "$log\n";
-        } else {
-            syslog ('info', "%s", $log);
-        }
-    }
-}
-
-##############################################################################
-# Parameter checking
-##############################################################################
-
-# Check all arguments against a very restricted set of allowed characters and
-# to ensure the right number of arguments are taken.  The arguments are the
-# number of arguments expected (minimum and maximum), a reference to an array
-# of which argument numbers shouldn't be checked, and then the arguments.
-#
-# This function is probably temporary and will be replaced with something that
-# knows more about the syntax of each command and can check more things.
-sub check_args {
-    my ($min, $max, $exclude, @args) = @_;
-    if (@args &lt; $min) {
-        error "insufficient arguments";
-    } elsif (@args &gt; $max and $max != -1) {
-        error "too many arguments";
-    }
-    my %exclude = map { $_ =&gt; 1 } @$exclude;
-    for (my $i = 1; $i &lt;= @args; $i++) {
-        next if $exclude{$i};
-        unless ($args[$i - 1] =~ m,^[\w_/\@.-]*\z,) {
-            error "invalid characters in argument: $args[$i - 1]";
-        }
-    }
-}
-
-##############################################################################
-# Implementation
-##############################################################################
-
-# Parse and execute a command.  We wrap this in a subroutine call for easier
-# testing.
-sub command {
-    log_init;
-    my $user = $ENV{REMOTE_USER} or error "REMOTE_USER not set";
-    my $host = $ENV{REMOTE_HOST} || $ENV{REMOTE_ADDR}
-        or error "neither REMOTE_HOST nor REMOTE_ADDR set";
-
-    # Instantiate the server object.
-    my $server = Wallet::Server-&gt;new ($user, $host);
-
-    # Parse command-line options and dispatch to the appropriate calls.
-    my ($command, @args) = @_;
-    if ($command eq 'acl') {
-        my $action = shift @args;
-        if ($action eq 'add') {
-            check_args (3, 3, [3], @args);
-            $server-&gt;acl_add (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'check') {
-            check_args (1, 1, [], @args);
-            my $status = $server-&gt;acl_check (@args);
-            if (!defined ($status)) {
-                failure ($server-&gt;error, @_);
-            } else {
-                print $status ? "yes\n" : "no\n";
-            }
-        } elsif ($action eq 'create') {
-            check_args (1, 1, [], @args);
-            $server-&gt;acl_create (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'destroy') {
-            check_args (1, 1, [], @args);
-            $server-&gt;acl_destroy (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'history') {
-            check_args (1, 1, [], @args);
-            my $output = $server-&gt;acl_history (@args);
-            if (defined $output) {
-                print $output;
-            } else {
-                failure ($server-&gt;error, @_);
-            }
-        } elsif ($action eq 'remove') {
-            check_args (3, 3, [3], @args);
-            $server-&gt;acl_remove (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'rename') {
-            check_args (2, 2, [], @args);
-            $server-&gt;acl_rename (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'replace') {
-            check_args (2, 2, [], @args);
-            $server-&gt;acl_replace (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'show') {
-            check_args (1, 1, [], @args);
-            my $output = $server-&gt;acl_show (@args);
-            if (defined $output) {
-                print $output;
-            } else {
-                failure ($server-&gt;error, @_);
-            }
-        } else {
-            error "unknown command acl $action";
-        }
-    } elsif ($command eq 'autocreate') {
-        check_args (2, 2, [], @args);
-        $server-&gt;autocreate (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'check') {
-        check_args (2, 2, [], @args);
-        my $status = $server-&gt;check (@args);
-        if (!defined ($status)) {
-            failure ($server-&gt;error, @_);
-        } else {
-            print $status ? "yes\n" : "no\n";
-        }
-    } elsif ($command eq 'comment') {
-        check_args (2, 3, [3], @args);
-        if (@args &gt; 2) {
-            $server-&gt;comment (@args) or failure ($server-&gt;error, @_);
-        } else {
-            my $output = $server-&gt;comment (@args);
-            if (defined $output) {
-                print $output, "\n";
-            } elsif (not $server-&gt;error) {
-                print "No comment set\n";
-            } else {
-                failure ($server-&gt;error, @_);
-            }
-        }
-    } elsif ($command eq 'create') {
-        check_args (2, 2, [], @args);
-        $server-&gt;create (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'destroy') {
-        check_args (2, 2, [], @args);
-        $server-&gt;destroy (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'expires') {
-        check_args (2, 3, [], @args);
-        if (@args &gt; 2) {
-            $server-&gt;expires (@args) or failure ($server-&gt;error, @_);
-        } else {
-            my $output = $server-&gt;expires (@args);
-            if (defined $output) {
-                print $output, "\n";
-            } elsif (not $server-&gt;error) {
-                print "No expiration set\n";
-            } else {
-                failure ($server-&gt;error, @_);
-            }
-        }
-    } elsif ($command eq 'flag') {
-        my $action = shift @args;
-        check_args (3, 3, [], @args);
-        if ($action eq 'clear') {
-            $server-&gt;flag_clear (@args) or failure ($server-&gt;error, @_);
-        } elsif ($action eq 'set') {
-            $server-&gt;flag_set (@args) or failure ($server-&gt;error, @_);
-        } else {
-            error "unknown command flag $action";
-        }
-    } elsif ($command eq 'get') {
-        check_args (2, 2, [], @args);
-        my $output = $server-&gt;get (@args);
-        if (defined $output) {
-            print $output;
-        } else {
-            failure ($server-&gt;error, @_);
-        }
-    } elsif ($command eq 'getacl') {
-        check_args (3, 3, [], @args);
-        my $output = $server-&gt;acl (@args);
-        if (defined $output) {
-            print $output, "\n";
-        } elsif (not $server-&gt;error) {
-            print "No ACL set\n";
-        } else {
-            failure ($server-&gt;error, @_);
-        }
-    } elsif ($command eq 'getattr') {
-        check_args (3, 3, [], @args);
-        my @result = $server-&gt;attr (@args);
-        if (not @result and $server-&gt;error) {
-            failure ($server-&gt;error, @_);
-        } elsif (@result) {
-            print join ("\n", @result, '');
-        }
-    } elsif ($command eq 'history') {
-        check_args (2, 2, [], @args);
-        my $output = $server-&gt;history (@args);
-        if (defined $output) {
-            print $output;
-        } else {
-            failure ($server-&gt;error, @_);
-        }
-    } elsif ($command eq 'owner') {
-        check_args (2, 3, [], @args);
-        if (@args &gt; 2) {
-            $server-&gt;owner (@args) or failure ($server-&gt;error, @_);
-        } else {
-            my $output = $server-&gt;owner (@args);
-            if (defined $output) {
-                print $output, "\n";
-            } elsif (not $server-&gt;error) {
-                print "No owner set\n";
-            } else {
-                failure ($server-&gt;error, @_);
-            }
-        }
-    } elsif ($command eq 'rename') {
-        check_args (3, 3, [], @args);
-        $server-&gt;rename (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'setacl') {
-        check_args (4, 4, [], @args);
-        $server-&gt;acl (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'setattr') {
-        check_args (4, -1, [], @args);
-        $server-&gt;attr (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'show') {
-        check_args (2, 2, [], @args);
-        my $output = $server-&gt;show (@args);
-        if (defined $output) {
-            print $output;
-        } else {
-            failure ($server-&gt;error, @_);
-        }
-    } elsif ($command eq 'store') {
-        check_args (2, 3, [3], @args);
-        if (@args == 2) {
-            local $/;
-            $args[2] = &lt;STDIN&gt;;
-        }
-        splice (@_, 3);
-        $server-&gt;store (@args) or failure ($server-&gt;error, @_);
-    } elsif ($command eq 'update') {
-        check_args (2, 2, [], @args);
-        my $output = $server-&gt;update (@args);
-        if (defined $output) {
-            print $output;
-        } else {
-            failure ($server-&gt;error, @_);
-        }
-    } else {
-        error "unknown command $command";
-    }
-    success (@_);
-}
-
-# Parse command-line options.
-my ($quiet);
-Getopt::Long::config ('require_order');
-GetOptions ('q|quiet' =&gt; \$quiet) or exit 1;
-$SYSLOG = 0 if $quiet;
-
-# Run the command.
-command (@ARGV);
-
-__END__
-
-##############################################################################
-# Documentation
-##############################################################################
-
-# The commands section of this document is duplicated from the documentation
-# for wallet and should be kept in sync.
-
-=for stopwords
-wallet-backend backend backend-specific remctld ACL acl timestamp getacl
-setacl metadata keytab keytabs enctypes enctype ktadd KDC Allbery
-autocreate MERCHANTABILITY NONINFRINGEMENT sublicense
-
-=head1 NAME
-
-wallet-backend - Wallet server for storing and retrieving secure data
-
-=head1 SYNOPSIS
-
-B&lt;wallet-backend&gt; [B&lt;-q&gt;] I&lt;command&gt; [I&lt;args&gt; ...]
-
-=head1 DESCRIPTION
-
-B&lt;wallet-backend&gt; implements the interface between B&lt;remctld&gt; and the
-wallet system.  It is written to run under B&lt;remctld&gt; and expects the
-authenticated identity of the remote user in the REMOTE_USER environment
-variable.  It uses REMOTE_HOST or REMOTE_ADDR if REMOTE_HOST isn't set for
-additional trace information.  It accepts the command from B&lt;remctld&gt; on
-the command line, creates a Wallet::Server object, and calls the
-appropriate methods.
-
-This program is a fairly thin wrapper around Wallet::Server that
-translates command strings into method calls and returns the results.  It
-does check all arguments except for the &lt;data&gt; argument to the store
-command and rejects any argument not matching C&lt;^[\w_/.-]+\z&gt;; in other
-words, only alphanumerics, underscore (C&lt;_&gt;), slash (C&lt;/&gt;), period (C&lt;.&gt;),
-and hyphen (C&lt;-&gt;) are permitted in arguments.  This provides some
-additional security over and above the checking already done by the rest
-of the wallet code.
-
-=head1 OPTIONS
-
-=over 4
-
-=item B&lt;--quiet&gt;, B&lt;-q&gt;
-
-If this option is given, B&lt;wallet-backend&gt; will not log its actions to
-syslog.
-
-=back
-
-=head1 COMMANDS
-
-Most commands are only available to wallet administrators (users on the
-C&lt;ADMIN&gt; ACL).  The exceptions are C&lt;acl check&gt;, C&lt;check&gt;, C&lt;get&gt;,
-C&lt;store&gt;, C&lt;show&gt;, C&lt;destroy&gt;, C&lt;flag clear&gt;, C&lt;flag set&gt;, C&lt;getattr&gt;,
-C&lt;setattr&gt;, and C&lt;history&gt;.  C&lt;acl check&gt; and C&lt;check&gt; can be run by
-anyone.  All of the rest of those commands have their own ACLs except
-C&lt;getattr&gt; and C&lt;history&gt;, which use the C&lt;show&gt; ACL, C&lt;setattr&gt;, which
-uses the C&lt;store&gt; ACL, and C&lt;comment&gt;, which uses the owner or C&lt;show&gt; ACL
-depending on whether one is setting or retrieving the comment.  If the
-appropriate ACL is set, it alone is checked to see if the user has access.
-Otherwise, C&lt;destroy&gt;, C&lt;get&gt;, C&lt;store&gt;, C&lt;show&gt;, C&lt;getattr&gt;, C&lt;setattr&gt;,
-C&lt;history&gt;, and C&lt;comment&gt; access is permitted if the user is authorized
-by the owner ACL of the object.
-
-Administrators can run any command on any object or ACL except for C&lt;get&gt;
-and C&lt;store&gt;.  For C&lt;get&gt; and C&lt;store&gt;, they must still be authorized by
-either the appropriate specific ACL or the owner ACL.
-
-If the locked flag is set on an object, no commands can be run on that
-object that change data except the C&lt;flags&gt; commands, nor can the C&lt;get&gt;
-command be used on that object.  C&lt;show&gt;, C&lt;history&gt;, C&lt;getacl&gt;,
-C&lt;getattr&gt;, and C&lt;owner&gt;, C&lt;comment&gt;, or C&lt;expires&gt; without an argument
-can still be used on that object.
-
-For more information on attributes, see L&lt;ATTRIBUTES&gt;.
-
-=over 4
-
-=item acl add &lt;id&gt; &lt;scheme&gt; &lt;identifier&gt;
-
-Add an entry with &lt;scheme&gt; and &lt;identifier&gt; to the ACL &lt;id&gt;.  &lt;id&gt; may be
-either the name of an ACL or its numeric identifier.
-
-=item acl check &lt;id&gt;
-
-Check whether an ACL with the ID &lt;id&gt; already exists.  If it does, prints
-C&lt;yes&gt;; if not, prints C&lt;no&gt;.
-
-=item acl create &lt;name&gt;
-
-Create a new, empty ACL with name &lt;name&gt;.  When setting an ACL on an
-object with a set of entries that don't match an existing ACL, first
-create a new ACL with C&lt;acl create&gt;, add the appropriate entries to it
-with C&lt;acl add&gt;, and then set the ACL on an object with the C&lt;owner&gt; or
-C&lt;setacl&gt; commands.
-
-=item acl destroy &lt;id&gt;
-
-Destroy the ACL &lt;id&gt;.  This ACL must no longer be referenced by any object
-or the ACL destruction will fail.  The special ACL named C&lt;ADMIN&gt; cannot
-be destroyed.
-
-=item acl history &lt;id&gt;
-
-Display the history of the ACL &lt;id&gt;.  Each change to the ACL (not
-including changes to the name of the ACL) will be represented by two
-lines.  The first line will have a timestamp of the change followed by a
-description of the change, and the second line will give the user who made
-the change and the host from which the change was made.
-
-=item acl remove &lt;id&gt; &lt;scheme&gt; &lt;identifier&gt;
-
-Remove the entry with &lt;scheme&gt; and &lt;identifier&gt; from the ACL &lt;id&gt;.  &lt;id&gt;
-may be either the name of an ACL or its numeric identifier.  The last
-entry in the special ACL C&lt;ADMIN&gt; cannot be removed to protect against
-accidental lockout, but administrators can remove themselves from the
-C&lt;ADMIN&gt; ACL and can leave only a non-functioning entry on the ACL.  Use
-caution when removing entries from the C&lt;ADMIN&gt; ACL.
-
-=item acl rename &lt;id&gt; &lt;name&gt;
-
-Renames the ACL identified by &lt;id&gt; to &lt;name&gt;.  This changes the
-human-readable name, not the underlying numeric ID, so the ACL's
-associations with objects will be unchanged.  The C&lt;ADMIN&gt; ACL may not be
-renamed.  &lt;id&gt; may be either the current name or the numeric ID.  &lt;name&gt;
-must not be all-numeric.  To rename an ACL, the current user must be
-authorized by the C&lt;ADMIN&gt; ACL.
-
-=item acl replace &lt;id&gt; &lt;new-id&gt;
-
-Find any objects owned by &lt;id&gt;, and then change their ownership to
-&lt;new_id&gt; instead.  &lt;new-id&gt; should already exist, and may already have
-some objects owned by it.  &lt;id&gt; is not deleted afterwards, though in
-most cases that is probably your next step.  The C&lt;ADMIN&gt; ACL may not be
-replaced from.  &lt;id&gt; and &lt;new-id&gt; may be either the current name or the
-numeric ID.  To replace an ACL, the current user must be authorized by
-the C&lt;ADMIN&gt; ACL.
-
-=item acl show &lt;id&gt;
-
-Display the name, numeric ID, and entries of the ACL &lt;id&gt;.
-
-=item autocreate &lt;type&gt; &lt;name&gt;
-
-Create a new object of type &lt;type&gt; with name &lt;name&gt;.  The user must be
-listed in the default ACL for an object with that type and name, and the
-object will be created with that default ACL set as the object owner.
-
-=item check &lt;type&gt; &lt;name&gt;
-
-Check whether an object of type &lt;type&gt; and name &lt;name&gt; already exists.  If
-it does, prints C&lt;yes&gt;; if not, prints C&lt;no&gt;.
-
-=item comment &lt;type&gt; &lt;name&gt; [&lt;comment&gt;]
-
-If &lt;comment&gt; is not given, displays the current comment for the object
-identified by &lt;type&gt; and &lt;name&gt;, or C&lt;No comment set&gt; if none is set.
-
-If &lt;comment&gt; is given, sets the comment on the object identified by
-&lt;type&gt; and &lt;name&gt; to &lt;comment&gt;.  If &lt;comment&gt; is the empty string, clears
-the comment.
-
-=item create &lt;type&gt; &lt;name&gt;
-
-Create a new object of type &lt;type&gt; with name &lt;name&gt;.  With some backends,
-this will trigger creation of an entry in an external system as well.
-The new object will have no ACLs and no owner set, so usually the
-administrator will want to then set an owner with C&lt;owner&gt; so that the
-object will be usable.
-
-=item destroy &lt;type&gt; &lt;name&gt;
-
-Destroy the object identified by &lt;type&gt; and &lt;name&gt;.  With some backends,
-this will trigger destruction of an object in an external system as well.
-
-=item expires &lt;type&gt; &lt;name&gt; [&lt;date&gt; [&lt;time&gt;]]
-
-If &lt;date&gt; is not given, displays the current expiration of the object
-identified by &lt;type&gt; and &lt;name&gt;, or C&lt;No expiration set&gt; if none is set.
-The expiration will be displayed in seconds since epoch.
-
-If &lt;date&gt; is given, sets the expiration on the object identified by &lt;type&gt;
-and &lt;name&gt; to &lt;date&gt; and (if given) &lt;time&gt;.  &lt;date&gt; and &lt;time&gt; must be in
-some format that can be parsed by the Perl Date::Parse module.  Most
-common formats are supported; if in doubt, use C&lt;YYYY-MM-DD HH:MM:SS&gt;.  If
-&lt;date&gt; is the empty string, clears the expiration of the object.
-
-Currently, the expiration of an object is not used.
-
-=item flag clear &lt;type&gt; &lt;name&gt; &lt;flag&gt;
-
-Clears the flag &lt;flag&gt; on the object identified by &lt;type&gt; and &lt;name&gt;.
-
-=item flag set &lt;type&gt; &lt;name&gt; &lt;flag&gt;
-
-Sets the flag &lt;flag&gt; on the object identified by &lt;type&gt; and &lt;name&gt;.
-Recognized flags are C&lt;locked&gt;, which prevents all further actions on that
-object until the flag is cleared, and C&lt;unchanging&gt;, which tells the
-object backend to not generate new data on get but instead return the same
-data as previously returned.  The C&lt;unchanging&gt; flag is not meaningful for
-objects that do not generate new data on the fly.
-
-=item get &lt;type&gt; &lt;name&gt;
-
-Prints to standard output the data associated with the object identified
-by &lt;type&gt; and &lt;name&gt;.  This may trigger generation of new data and
-invalidate old data for that object depending on the object type.
-
-=item getacl &lt;type&gt; &lt;name&gt; &lt;acl&gt;
-
-Prints the ACL &lt;acl&gt;, which must be one of C&lt;get&gt;, C&lt;store&gt;, C&lt;show&gt;,
-C&lt;destroy&gt;, or C&lt;flags&gt;, for the object identified by &lt;type&gt; and &lt;name&gt;.
-Prints C&lt;No ACL set&gt; if that ACL isn't set on that object.  Remember that
-if the C&lt;get&gt;, C&lt;store&gt;, or C&lt;show&gt; ACLs aren't set, authorization falls
-back to checking the owner ACL.  See the C&lt;owner&gt; command for displaying
-or setting it.
-
-=item getattr &lt;type&gt; &lt;name&gt; &lt;attr&gt;
-
-Prints the object attribute &lt;attr&gt; for the object identified by &lt;type&gt; and
-&lt;name&gt;.  Attributes are used to store backend-specific information for a
-particular object type, and &lt;attr&gt; must be an attribute type known to the
-underlying object implementation.  The attribute values, if any, are
-printed one per line.  If the attribute is not set on this object, nothing
-is printed.
-
-=item history &lt;type&gt; &lt;name&gt;
-
-Displays the history for the object identified by &lt;type&gt; and &lt;name&gt;.  This
-human-readable output will have two lines for each action that changes the
-object, plus for any get action.  The first line has the timestamp of the
-action and the action, and the second line gives the user who performed
-the action and the host from which they performed it.
-
-=item owner &lt;type&gt; &lt;name&gt; [&lt;owner&gt;]
-
-If &lt;owner&gt; is not given, displays the current owner ACL of the object
-identified by &lt;type&gt; and &lt;name&gt;, or C&lt;No owner set&gt; if none is set.  The
-result will be the name of an ACL.
-
-If &lt;owner&gt; is given, sets the owner of the object identified by &lt;type&gt; and
-&lt;name&gt; to &lt;owner&gt;.  If &lt;owner&gt; is the empty string, clears the owner of
-the object.
-
-=item rename &lt;type&gt; &lt;name&gt; &lt;new-name&gt;
-
-Renames an existing object.  This currently only supports file objects,
-where it renames the object itself, then the name and location of the
-object in the file store.
-
-=item setacl &lt;type&gt; &lt;name&gt; &lt;acl&gt; &lt;id&gt;
-
-Sets the ACL &lt;acl&gt;, which must be one of C&lt;get&gt;, C&lt;store&gt;, C&lt;show&gt;,
-C&lt;destroy&gt;, or C&lt;flags&gt;, to &lt;id&gt; on the object identified by &lt;type&gt; and
-&lt;name&gt;.  If &lt;id&gt; is the empty string, clears that ACL on the object.
-
-=item setattr &lt;type&gt; &lt;name&gt; &lt;attr&gt; &lt;value&gt; [&lt;value&gt; ...]
-
-Sets the object attribute &lt;attr&gt; for the object identified by &lt;type&gt; and
-&lt;name&gt;.  Attributes are used to store backend-specific information for a
-particular object type, and &lt;attr&gt; must be an attribute type known to the
-underlying object implementation.  To clear the attribute for this object,
-pass in a &lt;value&gt; of the empty string (C&lt;''&gt;).
-
-=item show &lt;type&gt; &lt;name&gt;
-
-Displays the current object metadata for the object identified by &lt;type&gt;
-and &lt;name&gt;.  This human-readable output will show the object type and
-name, the owner, any specific ACLs set on the object, the expiration if
-any, and the user, remote host, and time when the object was created, last
-stored, and last downloaded.
-
-=item store &lt;type&gt; &lt;name&gt; [&lt;data&gt;]
-
-Stores &lt;data&gt; for the object identified by &lt;type&gt; and &lt;name&gt; for later
-retrieval with C&lt;get&gt;.  Not all object types support this.  If &lt;data&gt; is
-not given as an argument, it will be read from standard input.
-
-=item update &lt;type&gt; &lt;name&gt;
-
-Prints to standard output the data associated with the object identified
-by &lt;type&gt; and &lt;name&gt;.  If the object is one that can have changing
-information, such as a keytab or password, then we generate new data for
-that object regardless of whether there is current data or the unchanging
-flag is set.
-
-=back
-
-=head1 ATTRIBUTES
-
-Object attributes store additional properties and configuration
-information for objects stored in the wallet.  They are displayed as part
-of the object data with C&lt;show&gt;, retrieved with C&lt;getattr&gt;, and set with
-C&lt;setattr&gt;.
-
-=head2 Keytab Attributes
-
-Keytab objects support the following attributes:
-
-=over 4
-
-=item enctypes
-
-Restricts the generated keytab to a specific set of encryption types.  The
-values of this attribute must be enctype strings recognized by Kerberos
-(strings like C&lt;aes256-cts-hmac-sha1-96&gt; or C&lt;des-cbc-crc&gt;).  Note that
-the salt should not be included; since the salt is irrelevant for keytab
-keys, it will always be set to C&lt;normal&gt; by the wallet.
-
-If this attribute is set, the specified enctype list will be passed to
-ktadd when get() is called for that keytab.  If it is not set, the default
-set in the KDC will be used.
-
-This attribute is ignored if the C&lt;unchanging&gt; flag is set on a keytab.
-Keytabs retrieved with C&lt;unchanging&gt; set will contain all keys present in
-the KDC for that Kerberos principal and therefore may contain different
-enctypes than those requested by this attribute.
-
-=back
-
-=head1 AUTHOR
-
-Russ Allbery &lt;eagle@eyrie.org&gt;
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright 2007, 2008, 2010, 2011, 2012, 2013 The Board of Trustees of the
-Leland Stanford Junior University
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-=head1 SEE ALSO
-
-Wallet::Server(3), remctld(8)
-
-This program is part of the wallet system.  The current version is
-available from L&lt;http://www.eyrie.org/~eagle/software/wallet/&gt;.
-
-=cut
--- /dev/null	2016-01-23 14:00:27.000000000 -0800
+++ server/wallet-backend.in	2016-01-17 19:13:02.000000000 -0800
@@ -0,0 +1,695 @@
+#!@PERL@
+#
+# Wallet server for storing and retrieving secure data.
+
+use 5.008;
+use strict;
+use warnings;
+
+use Getopt::Long qw(GetOptions);
+use Sys::Syslog qw(openlog syslog);
+use Wallet::Server;
+
+# Set to zero to suppress syslog logging, which is used for testing and for
+# the -q option.  Set to a reference to a string to append messages to that
+# string instead.
+our $SYSLOG;
+$SYSLOG = 1 unless defined $SYSLOG;
+
+##############################################################################
+# Logging
+##############################################################################
+
+# Initialize logging.
+sub log_init {
+    if (ref $SYSLOG) {
+        $$SYSLOG = '';
+    } elsif ($SYSLOG) {
+        openlog ('wallet-backend', 'pid', 'auth');
+    }
+}
+
+# Get an identity string for the user suitable for including in log messages.
+sub identity {
+    my $identity = '';
+    if ($ENV{REMOTE_USER}) {
+        $identity = $ENV{REMOTE_USER};
+        my $host = $ENV{REMOTE_HOST} || $ENV{REMOTE_ADDR};
+        $identity .= " ($host)" if $host;
+    }
+    return $identity;
+}
+
+# Log an error message to both syslog and to stderr and exit with a non-zero
+# status.
+sub error {
+    my $message = join ('', @_);
+    if ($SYSLOG) {
+        my $identity = identity;
+        my $log;
+        if ($identity) {
+            $log = "error for $identity: $message";
+        } else {
+            $log = "error: $message";
+        }
+        $log =~ s/[^\x20-\x7e]/_/g;
+        if (ref $SYSLOG) {
+            $$SYSLOG .= "$log\n";
+        } else {
+            syslog ('err', "%s", $log);
+        }
+    }
+    die "$message\n";
+}
+
+# Log a wallet failure message for a given command to both syslog and to
+# stderr and exit with a non-zero status.  Takes the message and the command
+# that was being run.
+sub failure {
+    my ($message, @command) = @_;
+    if ($SYSLOG) {
+        my $log = "command @command from " . identity . " failed: $message";
+        $log =~ s/[^\x20-\x7e]/_/g;
+        if (ref $SYSLOG) {
+            $$SYSLOG .= "$log\n";
+        } else {
+            syslog ('err', "%s", $log);
+        }
+    }
+    die "$message\n";
+}
+
+# Log a wallet success message for a given command.
+sub success {
+    my (@command) = @_;
+    if ($SYSLOG) {
+        my $log = "command @command from " . identity . " succeeded";
+        $log =~ s/[^\x20-\x7e]/_/g;
+        if (ref $SYSLOG) {
+            $$SYSLOG .= "$log\n";
+        } else {
+            syslog ('info', "%s", $log);
+        }
+    }
+}
+
+##############################################################################
+# Parameter checking
+##############################################################################
+
+# Check all arguments against a very restricted set of allowed characters and
+# to ensure the right number of arguments are taken.  The arguments are the
+# number of arguments expected (minimum and maximum), a reference to an array
+# of which argument numbers shouldn't be checked, and then the arguments.
+#
+# This function is probably temporary and will be replaced with something that
+# knows more about the syntax of each command and can check more things.
+sub check_args {
+    my ($min, $max, $exclude, @args) = @_;
+    if (@args &lt; $min) {
+        error "insufficient arguments";
+    } elsif (@args &gt; $max and $max != -1) {
+        error "too many arguments";
+    }
+    my %exclude = map { $_ =&gt; 1 } @$exclude;
+    for (my $i = 1; $i &lt;= @args; $i++) {
+        next if $exclude{$i};
+        unless ($args[$i - 1] =~ m,^[\w_/\@.-]*\z,) {
+            error "invalid characters in argument: $args[$i - 1]";
+        }
+    }
+}
+
+##############################################################################
+# Implementation
+##############################################################################
+
+# Parse and execute a command.  We wrap this in a subroutine call for easier
+# testing.
+sub command {
+    log_init;
+    my $user = $ENV{REMOTE_USER} or error "REMOTE_USER not set";
+    my $host = $ENV{REMOTE_HOST} || $ENV{REMOTE_ADDR}
+        or error "neither REMOTE_HOST nor REMOTE_ADDR set";
+
+    # Instantiate the server object.
+    my $server = Wallet::Server-&gt;new ($user, $host);
+
+    # Parse command-line options and dispatch to the appropriate calls.
+    my ($command, @args) = @_;
+    if ($command eq 'acl') {
+        my $action = shift @args;
+        if ($action eq 'add') {
+            check_args (3, 3, [3], @args);
+            $server-&gt;acl_add (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'check') {
+            check_args (1, 1, [], @args);
+            my $status = $server-&gt;acl_check (@args);
+            if (!defined ($status)) {
+                failure ($server-&gt;error, @_);
+            } else {
+                print $status ? "yes\n" : "no\n";
+            }
+        } elsif ($action eq 'create') {
+            check_args (1, 1, [], @args);
+            $server-&gt;acl_create (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'destroy') {
+            check_args (1, 1, [], @args);
+            $server-&gt;acl_destroy (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'history') {
+            check_args (1, 1, [], @args);
+            my $output = $server-&gt;acl_history (@args);
+            if (defined $output) {
+                print $output;
+            } else {
+                failure ($server-&gt;error, @_);
+            }
+        } elsif ($action eq 'remove') {
+            check_args (3, 3, [3], @args);
+            $server-&gt;acl_remove (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'rename') {
+            check_args (2, 2, [], @args);
+            $server-&gt;acl_rename (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'replace') {
+            check_args (2, 2, [], @args);
+            $server-&gt;acl_replace (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'show') {
+            check_args (1, 1, [], @args);
+            my $output = $server-&gt;acl_show (@args);
+            if (defined $output) {
+                print $output;
+            } else {
+                failure ($server-&gt;error, @_);
+            }
+        } else {
+            error "unknown command acl $action";
+        }
+    } elsif ($command eq 'autocreate') {
+        check_args (2, 2, [], @args);
+        $server-&gt;autocreate (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'check') {
+        check_args (2, 2, [], @args);
+        my $status = $server-&gt;check (@args);
+        if (!defined ($status)) {
+            failure ($server-&gt;error, @_);
+        } else {
+            print $status ? "yes\n" : "no\n";
+        }
+    } elsif ($command eq 'comment') {
+        check_args (2, 3, [3], @args);
+        if (@args &gt; 2) {
+            $server-&gt;comment (@args) or failure ($server-&gt;error, @_);
+        } else {
+            my $output = $server-&gt;comment (@args);
+            if (defined $output) {
+                print $output, "\n";
+            } elsif (not $server-&gt;error) {
+                print "No comment set\n";
+            } else {
+                failure ($server-&gt;error, @_);
+            }
+        }
+    } elsif ($command eq 'create') {
+        check_args (2, 2, [], @args);
+        $server-&gt;create (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'destroy') {
+        check_args (2, 2, [], @args);
+        $server-&gt;destroy (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'expires') {
+        check_args (2, 3, [], @args);
+        if (@args &gt; 2) {
+            $server-&gt;expires (@args) or failure ($server-&gt;error, @_);
+        } else {
+            my $output = $server-&gt;expires (@args);
+            if (defined $output) {
+                print $output, "\n";
+            } elsif (not $server-&gt;error) {
+                print "No expiration set\n";
+            } else {
+                failure ($server-&gt;error, @_);
+            }
+        }
+    } elsif ($command eq 'flag') {
+        my $action = shift @args;
+        check_args (3, 3, [], @args);
+        if ($action eq 'clear') {
+            $server-&gt;flag_clear (@args) or failure ($server-&gt;error, @_);
+        } elsif ($action eq 'set') {
+            $server-&gt;flag_set (@args) or failure ($server-&gt;error, @_);
+        } else {
+            error "unknown command flag $action";
+        }
+    } elsif ($command eq 'get') {
+        check_args (2, 2, [], @args);
+        my $output = $server-&gt;get (@args);
+        if (defined $output) {
+            print $output;
+        } else {
+            failure ($server-&gt;error, @_);
+        }
+    } elsif ($command eq 'getacl') {
+        check_args (3, 3, [], @args);
+        my $output = $server-&gt;acl (@args);
+        if (defined $output) {
+            print $output, "\n";
+        } elsif (not $server-&gt;error) {
+            print "No ACL set\n";
+        } else {
+            failure ($server-&gt;error, @_);
+        }
+    } elsif ($command eq 'getattr') {
+        check_args (3, 3, [], @args);
+        my @result = $server-&gt;attr (@args);
+        if (not @result and $server-&gt;error) {
+            failure ($server-&gt;error, @_);
+        } elsif (@result) {
+            print join ("\n", @result, '');
+        }
+    } elsif ($command eq 'history') {
+        check_args (2, 2, [], @args);
+        my $output = $server-&gt;history (@args);
+        if (defined $output) {
+            print $output;
+        } else {
+            failure ($server-&gt;error, @_);
+        }
+    } elsif ($command eq 'owner') {
+        check_args (2, 3, [], @args);
+        if (@args &gt; 2) {
+            $server-&gt;owner (@args) or failure ($server-&gt;error, @_);
+        } else {
+            my $output = $server-&gt;owner (@args);
+            if (defined $output) {
+                print $output, "\n";
+            } elsif (not $server-&gt;error) {
+                print "No owner set\n";
+            } else {
+                failure ($server-&gt;error, @_);
+            }
+        }
+    } elsif ($command eq 'rename') {
+        check_args (3, 3, [], @args);
+        $server-&gt;rename (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'setacl') {
+        check_args (4, 4, [], @args);
+        $server-&gt;acl (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'setattr') {
+        check_args (4, -1, [], @args);
+        $server-&gt;attr (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'show') {
+        check_args (2, 2, [], @args);
+        my $output = $server-&gt;show (@args);
+        if (defined $output) {
+            print $output;
+        } else {
+            failure ($server-&gt;error, @_);
+        }
+    } elsif ($command eq 'store') {
+        check_args (2, 3, [3], @args);
+        if (@args == 2) {
+            local $/;
+            $args[2] = &lt;STDIN&gt;;
+        }
+        splice (@_, 3);
+        $server-&gt;store (@args) or failure ($server-&gt;error, @_);
+    } elsif ($command eq 'update') {
+        check_args (2, 2, [], @args);
+        my $output = $server-&gt;update (@args);
+        if (defined $output) {
+            print $output;
+        } else {
+            failure ($server-&gt;error, @_);
+        }
+    } else {
+        error "unknown command $command";
+    }
+    success (@_);
+}
+
+# Parse command-line options.
+my ($quiet);
+Getopt::Long::config ('require_order');
+GetOptions ('q|quiet' =&gt; \$quiet) or exit 1;
+$SYSLOG = 0 if $quiet;
+
+# Run the command.
+command (@ARGV);
+
+__END__
+
+##############################################################################
+# Documentation
+##############################################################################
+
+# The commands section of this document is duplicated from the documentation
+# for wallet and should be kept in sync.
+
+=for stopwords
+wallet-backend backend backend-specific remctld ACL acl timestamp getacl
+setacl metadata keytab keytabs enctypes enctype ktadd KDC Allbery
+autocreate MERCHANTABILITY NONINFRINGEMENT sublicense
+
+=head1 NAME
+
+wallet-backend - Wallet server for storing and retrieving secure data
+
+=head1 SYNOPSIS
+
+B&lt;wallet-backend&gt; [B&lt;-q&gt;] I&lt;command&gt; [I&lt;args&gt; ...]
+
+=head1 DESCRIPTION
+
+B&lt;wallet-backend&gt; implements the interface between B&lt;remctld&gt; and the
+wallet system.  It is written to run under B&lt;remctld&gt; and expects the
+authenticated identity of the remote user in the REMOTE_USER environment
+variable.  It uses REMOTE_HOST or REMOTE_ADDR if REMOTE_HOST isn't set for
+additional trace information.  It accepts the command from B&lt;remctld&gt; on
+the command line, creates a Wallet::Server object, and calls the
+appropriate methods.
+
+This program is a fairly thin wrapper around Wallet::Server that
+translates command strings into method calls and returns the results.  It
+does check all arguments except for the &lt;data&gt; argument to the store
+command and rejects any argument not matching C&lt;^[\w_/.-]+\z&gt;; in other
+words, only alphanumerics, underscore (C&lt;_&gt;), slash (C&lt;/&gt;), period (C&lt;.&gt;),
+and hyphen (C&lt;-&gt;) are permitted in arguments.  This provides some
+additional security over and above the checking already done by the rest
+of the wallet code.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B&lt;--quiet&gt;, B&lt;-q&gt;
+
+If this option is given, B&lt;wallet-backend&gt; will not log its actions to
+syslog.
+
+=back
+
+=head1 COMMANDS
+
+Most commands are only available to wallet administrators (users on the
+C&lt;ADMIN&gt; ACL).  The exceptions are C&lt;acl check&gt;, C&lt;check&gt;, C&lt;get&gt;,
+C&lt;store&gt;, C&lt;show&gt;, C&lt;destroy&gt;, C&lt;flag clear&gt;, C&lt;flag set&gt;, C&lt;getattr&gt;,
+C&lt;setattr&gt;, and C&lt;history&gt;.  C&lt;acl check&gt; and C&lt;check&gt; can be run by
+anyone.  All of the rest of those commands have their own ACLs except
+C&lt;getattr&gt; and C&lt;history&gt;, which use the C&lt;show&gt; ACL, C&lt;setattr&gt;, which
+uses the C&lt;store&gt; ACL, and C&lt;comment&gt;, which uses the owner or C&lt;show&gt; ACL
+depending on whether one is setting or retrieving the comment.  If the
+appropriate ACL is set, it alone is checked to see if the user has access.
+Otherwise, C&lt;destroy&gt;, C&lt;get&gt;, C&lt;store&gt;, C&lt;show&gt;, C&lt;getattr&gt;, C&lt;setattr&gt;,
+C&lt;history&gt;, and C&lt;comment&gt; access is permitted if the user is authorized
+by the owner ACL of the object.
+
+Administrators can run any command on any object or ACL except for C&lt;get&gt;
+and C&lt;store&gt;.  For C&lt;get&gt; and C&lt;store&gt;, they must still be authorized by
+either the appropriate specific ACL or the owner ACL.
+
+If the locked flag is set on an object, no commands can be run on that
+object that change data except the C&lt;flags&gt; commands, nor can the C&lt;get&gt;
+command be used on that object.  C&lt;show&gt;, C&lt;history&gt;, C&lt;getacl&gt;,
+C&lt;getattr&gt;, and C&lt;owner&gt;, C&lt;comment&gt;, or C&lt;expires&gt; without an argument
+can still be used on that object.
+
+For more information on attributes, see L&lt;ATTRIBUTES&gt;.
+
+=over 4
+
+=item acl add &lt;id&gt; &lt;scheme&gt; &lt;identifier&gt;
+
+Add an entry with &lt;scheme&gt; and &lt;identifier&gt; to the ACL &lt;id&gt;.  &lt;id&gt; may be
+either the name of an ACL or its numeric identifier.
+
+=item acl check &lt;id&gt;
+
+Check whether an ACL with the ID &lt;id&gt; already exists.  If it does, prints
+C&lt;yes&gt;; if not, prints C&lt;no&gt;.
+
+=item acl create &lt;name&gt;
+
+Create a new, empty ACL with name &lt;name&gt;.  When setting an ACL on an
+object with a set of entries that don't match an existing ACL, first
+create a new ACL with C&lt;acl create&gt;, add the appropriate entries to it
+with C&lt;acl add&gt;, and then set the ACL on an object with the C&lt;owner&gt; or
+C&lt;setacl&gt; commands.
+
+=item acl destroy &lt;id&gt;
+
+Destroy the ACL &lt;id&gt;.  This ACL must no longer be referenced by any object
+or the ACL destruction will fail.  The special ACL named C&lt;ADMIN&gt; cannot
+be destroyed.
+
+=item acl history &lt;id&gt;
+
+Display the history of the ACL &lt;id&gt;.  Each change to the ACL (not
+including changes to the name of the ACL) will be represented by two
+lines.  The first line will have a timestamp of the change followed by a
+description of the change, and the second line will give the user who made
+the change and the host from which the change was made.
+
+=item acl remove &lt;id&gt; &lt;scheme&gt; &lt;identifier&gt;
+
+Remove the entry with &lt;scheme&gt; and &lt;identifier&gt; from the ACL &lt;id&gt;.  &lt;id&gt;
+may be either the name of an ACL or its numeric identifier.  The last
+entry in the special ACL C&lt;ADMIN&gt; cannot be removed to protect against
+accidental lockout, but administrators can remove themselves from the
+C&lt;ADMIN&gt; ACL and can leave only a non-functioning entry on the ACL.  Use
+caution when removing entries from the C&lt;ADMIN&gt; ACL.
+
+=item acl rename &lt;id&gt; &lt;name&gt;
+
+Renames the ACL identified by &lt;id&gt; to &lt;name&gt;.  This changes the
+human-readable name, not the underlying numeric ID, so the ACL's
+associations with objects will be unchanged.  The C&lt;ADMIN&gt; ACL may not be
+renamed.  &lt;id&gt; may be either the current name or the numeric ID.  &lt;name&gt;
+must not be all-numeric.  To rename an ACL, the current user must be
+authorized by the C&lt;ADMIN&gt; ACL.
+
+=item acl replace &lt;id&gt; &lt;new-id&gt;
+
+Find any objects owned by &lt;id&gt;, and then change their ownership to
+&lt;new_id&gt; instead.  &lt;new-id&gt; should already exist, and may already have
+some objects owned by it.  &lt;id&gt; is not deleted afterwards, though in
+most cases that is probably your next step.  The C&lt;ADMIN&gt; ACL may not be
+replaced from.  &lt;id&gt; and &lt;new-id&gt; may be either the current name or the
+numeric ID.  To replace an ACL, the current user must be authorized by
+the C&lt;ADMIN&gt; ACL.
+
+=item acl show &lt;id&gt;
+
+Display the name, numeric ID, and entries of the ACL &lt;id&gt;.
+
+=item autocreate &lt;type&gt; &lt;name&gt;
+
+Create a new object of type &lt;type&gt; with name &lt;name&gt;.  The user must be
+listed in the default ACL for an object with that type and name, and the
+object will be created with that default ACL set as the object owner.
+
+=item check &lt;type&gt; &lt;name&gt;
+
+Check whether an object of type &lt;type&gt; and name &lt;name&gt; already exists.  If
+it does, prints C&lt;yes&gt;; if not, prints C&lt;no&gt;.
+
+=item comment &lt;type&gt; &lt;name&gt; [&lt;comment&gt;]
+
+If &lt;comment&gt; is not given, displays the current comment for the object
+identified by &lt;type&gt; and &lt;name&gt;, or C&lt;No comment set&gt; if none is set.
+
+If &lt;comment&gt; is given, sets the comment on the object identified by
+&lt;type&gt; and &lt;name&gt; to &lt;comment&gt;.  If &lt;comment&gt; is the empty string, clears
+the comment.
+
+=item create &lt;type&gt; &lt;name&gt;
+
+Create a new object of type &lt;type&gt; with name &lt;name&gt;.  With some backends,
+this will trigger creation of an entry in an external system as well.
+The new object will have no ACLs and no owner set, so usually the
+administrator will want to then set an owner with C&lt;owner&gt; so that the
+object will be usable.
+
+=item destroy &lt;type&gt; &lt;name&gt;
+
+Destroy the object identified by &lt;type&gt; and &lt;name&gt;.  With some backends,
+this will trigger destruction of an object in an external system as well.
+
+=item expires &lt;type&gt; &lt;name&gt; [&lt;date&gt; [&lt;time&gt;]]
+
+If &lt;date&gt; is not given, displays the current expiration of the object
+identified by &lt;type&gt; and &lt;name&gt;, or C&lt;No expiration set&gt; if none is set.
+The expiration will be displayed in seconds since epoch.
+
+If &lt;date&gt; is given, sets the expiration on the object identified by &lt;type&gt;
+and &lt;name&gt; to &lt;date&gt; and (if given) &lt;time&gt;.  &lt;date&gt; and &lt;time&gt; must be in
+some format that can be parsed by the Perl Date::Parse module.  Most
+common formats are supported; if in doubt, use C&lt;YYYY-MM-DD HH:MM:SS&gt;.  If
+&lt;date&gt; is the empty string, clears the expiration of the object.
+
+Currently, the expiration of an object is not used.
+
+=item flag clear &lt;type&gt; &lt;name&gt; &lt;flag&gt;
+
+Clears the flag &lt;flag&gt; on the object identified by &lt;type&gt; and &lt;name&gt;.
+
+=item flag set &lt;type&gt; &lt;name&gt; &lt;flag&gt;
+
+Sets the flag &lt;flag&gt; on the object identified by &lt;type&gt; and &lt;name&gt;.
+Recognized flags are C&lt;locked&gt;, which prevents all further actions on that
+object until the flag is cleared, and C&lt;unchanging&gt;, which tells the
+object backend to not generate new data on get but instead return the same
+data as previously returned.  The C&lt;unchanging&gt; flag is not meaningful for
+objects that do not generate new data on the fly.
+
+=item get &lt;type&gt; &lt;name&gt;
+
+Prints to standard output the data associated with the object identified
+by &lt;type&gt; and &lt;name&gt;.  This may trigger generation of new data and
+invalidate old data for that object depending on the object type.
+
+=item getacl &lt;type&gt; &lt;name&gt; &lt;acl&gt;
+
+Prints the ACL &lt;acl&gt;, which must be one of C&lt;get&gt;, C&lt;store&gt;, C&lt;show&gt;,
+C&lt;destroy&gt;, or C&lt;flags&gt;, for the object identified by &lt;type&gt; and &lt;name&gt;.
+Prints C&lt;No ACL set&gt; if that ACL isn't set on that object.  Remember that
+if the C&lt;get&gt;, C&lt;store&gt;, or C&lt;show&gt; ACLs aren't set, authorization falls
+back to checking the owner ACL.  See the C&lt;owner&gt; command for displaying
+or setting it.
+
+=item getattr &lt;type&gt; &lt;name&gt; &lt;attr&gt;
+
+Prints the object attribute &lt;attr&gt; for the object identified by &lt;type&gt; and
+&lt;name&gt;.  Attributes are used to store backend-specific information for a
+particular object type, and &lt;attr&gt; must be an attribute type known to the
+underlying object implementation.  The attribute values, if any, are
+printed one per line.  If the attribute is not set on this object, nothing
+is printed.
+
+=item history &lt;type&gt; &lt;name&gt;
+
+Displays the history for the object identified by &lt;type&gt; and &lt;name&gt;.  This
+human-readable output will have two lines for each action that changes the
+object, plus for any get action.  The first line has the timestamp of the
+action and the action, and the second line gives the user who performed
+the action and the host from which they performed it.
+
+=item owner &lt;type&gt; &lt;name&gt; [&lt;owner&gt;]
+
+If &lt;owner&gt; is not given, displays the current owner ACL of the object
+identified by &lt;type&gt; and &lt;name&gt;, or C&lt;No owner set&gt; if none is set.  The
+result will be the name of an ACL.
+
+If &lt;owner&gt; is given, sets the owner of the object identified by &lt;type&gt; and
+&lt;name&gt; to &lt;owner&gt;.  If &lt;owner&gt; is the empty string, clears the owner of
+the object.
+
+=item rename &lt;type&gt; &lt;name&gt; &lt;new-name&gt;
+
+Renames an existing object.  This currently only supports file objects,
+where it renames the object itself, then the name and location of the
+object in the file store.
+
+=item setacl &lt;type&gt; &lt;name&gt; &lt;acl&gt; &lt;id&gt;
+
+Sets the ACL &lt;acl&gt;, which must be one of C&lt;get&gt;, C&lt;store&gt;, C&lt;show&gt;,
+C&lt;destroy&gt;, or C&lt;flags&gt;, to &lt;id&gt; on the object identified by &lt;type&gt; and
+&lt;name&gt;.  If &lt;id&gt; is the empty string, clears that ACL on the object.
+
+=item setattr &lt;type&gt; &lt;name&gt; &lt;attr&gt; &lt;value&gt; [&lt;value&gt; ...]
+
+Sets the object attribute &lt;attr&gt; for the object identified by &lt;type&gt; and
+&lt;name&gt;.  Attributes are used to store backend-specific information for a
+particular object type, and &lt;attr&gt; must be an attribute type known to the
+underlying object implementation.  To clear the attribute for this object,
+pass in a &lt;value&gt; of the empty string (C&lt;''&gt;).
+
+=item show &lt;type&gt; &lt;name&gt;
+
+Displays the current object metadata for the object identified by &lt;type&gt;
+and &lt;name&gt;.  This human-readable output will show the object type and
+name, the owner, any specific ACLs set on the object, the expiration if
+any, and the user, remote host, and time when the object was created, last
+stored, and last downloaded.
+
+=item store &lt;type&gt; &lt;name&gt; [&lt;data&gt;]
+
+Stores &lt;data&gt; for the object identified by &lt;type&gt; and &lt;name&gt; for later
+retrieval with C&lt;get&gt;.  Not all object types support this.  If &lt;data&gt; is
+not given as an argument, it will be read from standard input.
+
+=item update &lt;type&gt; &lt;name&gt;
+
+Prints to standard output the data associated with the object identified
+by &lt;type&gt; and &lt;name&gt;.  If the object is one that can have changing
+information, such as a keytab or password, then we generate new data for
+that object regardless of whether there is current data or the unchanging
+flag is set.
+
+=back
+
+=head1 ATTRIBUTES
+
+Object attributes store additional properties and configuration
+information for objects stored in the wallet.  They are displayed as part
+of the object data with C&lt;show&gt;, retrieved with C&lt;getattr&gt;, and set with
+C&lt;setattr&gt;.
+
+=head2 Keytab Attributes
+
+Keytab objects support the following attributes:
+
+=over 4
+
+=item enctypes
+
+Restricts the generated keytab to a specific set of encryption types.  The
+values of this attribute must be enctype strings recognized by Kerberos
+(strings like C&lt;aes256-cts-hmac-sha1-96&gt; or C&lt;des-cbc-crc&gt;).  Note that
+the salt should not be included; since the salt is irrelevant for keytab
+keys, it will always be set to C&lt;normal&gt; by the wallet.
+
+If this attribute is set, the specified enctype list will be passed to
+ktadd when get() is called for that keytab.  If it is not set, the default
+set in the KDC will be used.
+
+This attribute is ignored if the C&lt;unchanging&gt; flag is set on a keytab.
+Keytabs retrieved with C&lt;unchanging&gt; set will contain all keys present in
+the KDC for that Kerberos principal and therefore may contain different
+enctypes than those requested by this attribute.
+
+=back
+
+=head1 AUTHOR
+
+Russ Allbery &lt;eagle@eyrie.org&gt;
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007, 2008, 2010, 2011, 2012, 2013 The Board of Trustees of the
+Leland Stanford Junior University
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+=head1 SEE ALSO
+
+Wallet::Server(3), remctld(8)
+
+This program is part of the wallet system.  The current version is
+available from L&lt;http://www.eyrie.org/~eagle/software/wallet/&gt;.
+
+=cut
</pre></body></html>