aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/patches/0001-allow-quilt.patch25
-rw-r--r--debian/patches/0002-password-charset.patch40
-rw-r--r--debian/patches/0003-checksum.patch124
-rw-r--r--debian/patches/0004-news.patch24
-rw-r--r--debian/patches/0005-checksum-doc.patch17
-rw-r--r--debian/patches/0006-file-crypt.patch239
-rw-r--r--debian/patches/0007-class-error.patch30
-rw-r--r--debian/patches/0008-invalid-ldap-url.patch17
-rw-r--r--debian/patches/0009-require-perl-gssapi.patch17
-rw-r--r--debian/patches/0010-empty-store.patch110
-rw-r--r--debian/patches/0011-doc.patch168
-rw-r--r--debian/patches/0012-pwd-options.patch130
-rw-r--r--debian/patches/0013-crypt-fixup.patch117
-rw-r--r--debian/patches/0014-crypt-custom.patch210
-rw-r--r--debian/patches/0015-encryption-fixes.patch227
-rw-r--r--debian/patches/0016-checkfile.patch841
-rw-r--r--debian/patches/0017-checkfile.patch77
-rw-r--r--debian/patches/0018-ad-length.patch36
-rw-r--r--debian/patches/0019-password-encrypt.patch122
-rw-r--r--debian/patches/0020-retire-webauth.patch1359
-rw-r--r--debian/patches/0021-spx-test.patch12
-rw-r--r--debian/patches/0022-password-double-encryption.patch12
-rw-r--r--debian/patches/0023-ldap-attr-filter.patch183
-rw-r--r--debian/patches/series23
24 files changed, 4160 insertions, 0 deletions
diff --git a/debian/patches/0001-allow-quilt.patch b/debian/patches/0001-allow-quilt.patch
new file mode 100644
index 0000000..1edd0f4
--- /dev/null
+++ b/debian/patches/0001-allow-quilt.patch
@@ -0,0 +1,25 @@
+diff --git a/tests/docs/spdx-license-t b/tests/docs/spdx-license-t
+index 91072bf..a55b054 100755
+--- a/tests/docs/spdx-license-t
++++ b/tests/docs/spdx-license-t
+@@ -72,6 +72,7 @@ my @IGNORE_PATHS = (
+ qr{ \A php/ltmain [.] sh \z }xms, # Created by phpize
+ qr{ \A php/run-tests [.] php \z }xms, # Created by phpize
+ qr{ [.] l?a \z }xms, # Created by libtool
++ qr{ [.] pc/ }xms, # Created by quilt
+ );
+ ## use critic
+
+diff --git a/tests/tap/perl/Test/RRA/Automake.pm b/tests/tap/perl/Test/RRA/Automake.pm
+index 69fff6a..1a785ff 100644
+--- a/tests/tap/perl/Test/RRA/Automake.pm
++++ b/tests/tap/perl/Test/RRA/Automake.pm
+@@ -74,7 +74,7 @@ BEGIN {
+ # Directories to skip globally when looking for all files, or for directories
+ # that could contain Perl files.
+ my @GLOBAL_SKIP = qw(
+- .git _build autom4te.cache build-aux perl/_build perl/blib
++ .git _build autom4te.cache build-aux perl/_build perl/blib debian .pc
+ );
+
+ # Additional paths to skip when building a list of all files in the
diff --git a/debian/patches/0002-password-charset.patch b/debian/patches/0002-password-charset.patch
new file mode 100644
index 0000000..00badcb
--- /dev/null
+++ b/debian/patches/0002-password-charset.patch
@@ -0,0 +1,40 @@
+diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm
+index 60f0e10..66d433f 100644
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -298,6 +298,15 @@ is run before data is stored.
+
+ our $PWD_LENGTH_MAX = 21;
+
++=item PWD_CHARACTERS
++
++A string that contains valid characters to be used in generating
++passwords. The default is to allow any printable character.
++
++=cut
++
++our $PWD_CHARACTERS = '';
++
+ =back
+
+ =head1 KEYTAB OBJECT CONFIGURATION
+diff --git a/perl/lib/Wallet/Object/Password.pm b/perl/lib/Wallet/Object/Password.pm
+index 336aa9d..f581c18 100644
+--- a/perl/lib/Wallet/Object/Password.pm
++++ b/perl/lib/Wallet/Object/Password.pm
+@@ -81,6 +81,15 @@ sub retrieve {
+ }
+ my $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
+ $Wallet::Config::PWD_LENGTH_MAX);
++ if ($Wallet::Config::PWD_CHARACTERS) {
++ my @pw_chars = ();
++ for (my $i=0; $i<length($Wallet::Config::PWD_CHARACTERS); $i++) {
++ push @pw_chars, substr($Wallet::Config::PWD_CHARACTERS, $i, 1);
++ }
++ $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
++ $Wallet::Config::PWD_LENGTH_MAX,
++ \@pw_chars);
++ }
+ print FILE $pass;
+ $self->log_action ('store', $user, $host, $time);
+ unless (close FILE) {
diff --git a/debian/patches/0003-checksum.patch b/debian/patches/0003-checksum.patch
new file mode 100644
index 0000000..514d93a
--- /dev/null
+++ b/debian/patches/0003-checksum.patch
@@ -0,0 +1,124 @@
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -1038,6 +1038,25 @@ Obvious improvements could be made, such
+ the slash for a C<host/> ACL looked like a host name and the part after a
+ slash for a C<user/> ACL look like a user name.
+
++=head1 FILE CHECKSUMS
++
++By default a file objects checksum some will be calculated using the
++perl function md5_hex of the Digest::MD5 module. This behavior can be
++overriden by defining a perl function in the configuration file named
++file_checksum that returns a checksum for the file.
++
++For example, the following file_checksub function returns the MD5 hash
++as a base64 string.
++
++ sub file_checksum {
++ my ($path) = @_;
++ open(my $fh, '<', $path) or die "ERROR: reading $filename";
++ binmode($fh);
++ my $cs = Digest::MD5->new->addfile($fh)->b64digest, "\n";
++ close $fh;
++ return $cs;
++ }
++
+ =head1 ENVIRONMENT
+
+ =over 4
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -19,6 +19,7 @@ use warnings;
+
+ use Digest::MD5 qw(md5_hex);
+ use File::Copy qw(move);
++use File::Slurp;
+ use Wallet::Config;
+ use Wallet::Object::Base;
+
+@@ -146,6 +147,26 @@ sub get {
+ return $data;
+ }
+
++# Return an check sum of a file
++sub checksum {
++ my ($self, $user, $host, $time) = @_;
++ $time ||= time;
++ my $id = $self->{type} . ':' . $self->{name};
++ if ($self->flag_check ('locked')) {
++ $self->error ("cannot get $id: object is locked");
++ return;
++ }
++ my $path = $self->file_path;
++ my $this_checksum;
++ if (defined (&Wallet::Config::file_checksum)) {
++ $this_checksum = Wallet::Config::file_checksum($path);
++ } else {
++ $this_checksum = md5_hex(read_file($path));
++ }
++ $self->log_action ('checksum', $user, $host, $time);
++ return $this_checksum;
++}
++
+ # Store the file on the wallet server.
+ sub store {
+ my ($self, $data, $user, $host, $time) = @_;
+@@ -242,6 +263,13 @@ HOSTNAME, and DATETIME are stored as his
+ should be the user who is downloading the keytab. If DATETIME isn't
+ given, the current time is used.
+
++=item checksum(PRINCIPAL, HOSTNAME [, DATETIME])
++
++Retrieves the checksum for contents of the file object or undef on
++error. PRINCIPAL, HOSTNAME, and DATETIME are stored as history
++information. PRINCIPAL should be the user who is downloading the
++keytab. If DATETIME isn't given, the current time is used.
++
+ =item store(DATA, PRINCIPAL, HOSTNAME [, DATETIME])
+
+ Store DATA as the current contents of the file object. Any existing data
+--- a/server/wallet-backend.in
++++ b/server/wallet-backend.in
+@@ -196,6 +196,14 @@ sub command {
+ } else {
+ print $status ? "yes\n" : "no\n";
+ }
++ } elsif ($command eq 'checksum') {
++ check_args (2, 2, [], @args);
++ my $output = $server->checksum (@args);
++ if (defined $output) {
++ print $output;
++ } else {
++ failure ($server->error, @_);
++ }
+ } elsif ($command eq 'comment') {
+ check_args (2, 3, [3], @args);
+ if (@args > 2) {
+--- a/perl/lib/Wallet/Server.pm
++++ b/perl/lib/Wallet/Server.pm
+@@ -499,6 +499,25 @@ sub check {
+ return 1;
+ }
+
++# Returns the checksum for a file or password object.
++sub checksum {
++ my ($self, $type, $name) = @_;
++ if ($type ne 'file' && $type ne 'password') {
++ $self->error ("Invalid type ${type}");
++ return;
++ }
++ my $object = $self->retrieve ($type, $name);
++ if (!defined $object) {
++ return;
++ }
++ if (!$self->acl_verify ($object, 'get')) {
++ return;
++ }
++ my $result = $object->checksum($self->{user}, $self->{host});
++ $self->error ($object->error) unless defined $result;
++ return $result;
++}
++
+ # Retrieve the information associated with an object, or returns undef and
+ # sets the internal error if the retrieval fails or if the user isn't
+ # authorized. If the object doesn't exist, attempts dynamic creation of the
diff --git a/debian/patches/0004-news.patch b/debian/patches/0004-news.patch
new file mode 100644
index 0000000..2c11c18
--- /dev/null
+++ b/debian/patches/0004-news.patch
@@ -0,0 +1,24 @@
+diff --git a/NEWS b/NEWS
+index be636b4..a5f310b 100644
+--- a/NEWS
++++ b/NEWS
+@@ -1,5 +1,19 @@
+ User-Visible wallet Changes
+
++wallet 1.4-3 (2019-09-30)
++
++ Two new features have been added to wallet with this release.
++
++ A valid set of characters to be used when generating a password
++ can be specified. This allows for a variety of password policies
++ include use of only lower case characters, excluding the use of
++ upper case O, etc.
++
++ The checksum command had been added that returns the checksum of
++ a file object. This makes it simpler to integrate the use of
++ wallet with configuration management systems such as puppet and
++ chef.
++
+ wallet 1.4 (2018-06-03)
+
+ Substantial improvements to Active Directory support: Add a
diff --git a/debian/patches/0005-checksum-doc.patch b/debian/patches/0005-checksum-doc.patch
new file mode 100644
index 0000000..25cef84
--- /dev/null
+++ b/debian/patches/0005-checksum-doc.patch
@@ -0,0 +1,17 @@
+--- a/server/wallet-backend.in
++++ b/server/wallet-backend.in
+@@ -500,6 +500,14 @@
+ Check whether an object of type <type> and name <name> already exists. If
+ it does, prints C<yes>; if not, prints C<no>.
+
++=item checksum file|password <name>
++
++Return the checksum for a file or password object. By default a file
++objects checksum some will be calculated using the perl function
++md5_hex of the Digest::MD5 module. This behavior can be overriden in
++the wallet configuration file. See perldoc Wallet::Config for
++complete details.
++
+ =item comment <type> <name> [<comment>]
+
+ If <comment> is not given, displays the current comment for the object
diff --git a/debian/patches/0006-file-crypt.patch b/debian/patches/0006-file-crypt.patch
new file mode 100644
index 0000000..532b3ed
--- /dev/null
+++ b/debian/patches/0006-file-crypt.patch
@@ -0,0 +1,239 @@
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -765,11 +765,42 @@ with this ACL type. This variable must
+
+ =cut
+
++our $LDAP_SECRET;
++
++=item LDAP_SECRET
++
++Specifies an LDAP URL that is used to retrieve the secret to use when
++encrypting and decrypting file objects. The url must not include the
++hostname. LDAP_HOST will be used as the hostname to bind to. The
++Kerberos ticket cache specified in LDAP_CACHE is used when connecting
++to the LDAP server. GSS-API authentication is always used; there is
++currently no support for any other type of bind. The ticket cache
++must be for a principal with access to retrieve the secret. This
++variable and LDAP_CACHE must be set to use file object encryption.
++
++=cut
++
++our $LDAP_SECRET_PREFIX;
++
++=item LDAP_SECRET_PREFIX
++
++Specifies the prefix to be used when generating storing an encrypted
++file object. The prefix is used to determine whether or not a file
++object has been stored encrypted. This allows the gradual transition
++from unencrypted file objects to encrypted file objects. When file
++object encryption is enable any "get" of an unencyrpted file object
++will result in the replacement of the unencrypted object with an
++encrypted object.
++
++=cut
++
+ our $LDAP_CACHE;
+
+ =back
+
+-Finally, depending on the structure of the LDAP directory being queried,
++=head2 LDAP Principal Mapping
++
++Depending on the structure of the LDAP directory being queried,
+ there may not be any attribute in the directory whose value exactly
+ matches the Kerberos principal. The attribute designated by
+ LDAP_FILTER_ATTR may instead hold a transformation of the principal name
+@@ -794,6 +825,27 @@ Note that this example only removes the
+ Any principal from some other realm will be left fully qualified, and then
+ presumably will not be found in the directory.
+
++=head2 File Object Encryption
++
++The default encryption method use is based on the twofish cypher. If
++another encryption method is desired then the perl function file_crypt
++should be defined. The function must accept three parameters: the
++action to preform, the encryption secret, and the string to encrypt or
++decrypt. For example:
++
++ sub file_crypt {
++ use Crypt::RC4;
++ my ($action, $secret, $string) = @_;
++
++ my $return_string;
++ if ($action eq 'encrypt') {
++ $return_string = RC4($secret, $string);
++ } elsif ($action eq 'decrypt') {
++ $return_string = RC4($secret, $string);
++ }
++ return $return_string;
++ }
++
+ =head1 NETDB ACL CONFIGURATION
+
+ These configuration variables are only needed if you intend to use the
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -20,6 +20,7 @@ use warnings;
+ use Digest::MD5 qw(md5_hex);
+ use File::Copy qw(move);
+ use File::Slurp;
++use Wallet::ACL::LDAP::Attribute;
+ use Wallet::Config;
+ use Wallet::Object::Base;
+
+@@ -107,6 +108,114 @@ sub rename {
+ }
+
+ ##############################################################################
++# Encyption and decryption
++##############################################################################
++
++sub _get_crypt_key {
++ my ($self) = @_;
++
++ # ldap:///basedn?attr?scope?filter
++ my $url = $Wallet::Config::LDAP_SECRET;
++ $url =~ s{^ldap:///}{}xmsi;
++ if ($url eq $Wallet::Config::LDAP_SECRET) {
++ self->error("ERROR: Invalid LDAP URL $url");
++ exit 1;
++ }
++ my @parts = split /\?/, $url;
++ my $base = $parts[0];
++ my $attr = $parts[1];
++ my $scope = $parts[2];
++ my $filter = $parts[3];
++
++ # Search for the secret in the LDAP directory
++ my $ldap_obj = Wallet::ACL::LDAP::Attribute->new;
++ my $ldap = $ldap_obj->{ldap};
++ my $entry;
++ eval {
++ my @options = (base => $base,
++ filter => $filter,
++ scope => $scope,
++ attrs => [ $attr ]
++ );
++ my $search = $ldap->search (@options);
++ if ($search->count == 1) {
++ $entry = $search->pop_entry;
++ } elsif ($search->count > 1) {
++ die 'ERROR: ' . $search->count . " LDAP entries found for $filter";
++ } else {
++ die "ERROR: No entry found for $url";
++ }
++ };
++ if ($@ || !$entry) {
++ die "ERROR: LDAP search failed for $url";
++ }
++
++ my $return_val;
++ my $cnt = 0;
++ foreach my $return_attr ($entry->attributes) {
++ if (lc($return_attr) eq lc($attr)) {
++ $return_val = $entry->get_value($return_val);
++ $cnt++;
++ }
++ }
++ if (!$return_val) {
++ die "ERROR: LDAP search failed for $url";
++ }
++ if ($cnt !=1) {
++ die "ERROR: LDAP search return too many values ($url)";
++ }
++ return $return_val;
++}
++
++sub _file_crypt {
++ my ($self, $action, $string) = @_;
++
++ use Crypt::CBC;
++ use MIME::Base64;
++
++ my $return_string;
++ my $pre = $Wallet::Config::LDAP_SECRET_PREFIX;
++ my $key = $self->_get_crypt_key();
++
++ my $cipher = Crypt::CBC->new(
++ -key => $key,
++ -cipher => 'Twofish',
++ -padding => 'space',
++ -add_header => 1
++ );
++ if ($action eq 'encrypt') {
++ $return_string = $pre . encode_base64($cipher->encrypt($string));
++ } elsif ($action eq 'decrypt') {
++ if ($string =~ s/^$pre//xms) {
++ $return_string = $cipher->decrypt(decode_base64($string));
++ } else {
++ $return_string = $string;
++ }
++ } else {
++ my $msg = "ERROR: invalid action ($action)\n ";
++ $msg .= "INFO: action must be 'encrypt' or 'decrypt'\n";
++ $self->error($msg);
++ return;
++ }
++ return $return_string;
++}
++
++sub _file_decrypt {
++ my ($self, $data, $user, $host, $time) = @_;
++ my $undata = $self->_file_crypt('decrypt', $data);
++ if ($undata eq $data) {
++ $self->store($data, $user, $host, $time) = @_;
++ }
++ return $undata;
++}
++
++sub _file_encrypt {
++ my ($self, $data) = @_;
++ my $endata = $self->_file_crypt('encrypt', $data);
++ return $endata;
++}
++
++##############################################################################
+ # Core methods
+ ##############################################################################
+
+@@ -143,6 +253,9 @@ sub get {
+ $self->error ("cannot get $id: $!");
+ return;
+ }
++ if ($Wallet::LDAP::SECRET) {
++ $data = self->_file_decrypt($user, $host, $time, $data);
++ }
+ $self->log_action ('get', $user, $host, $time);
+ return $data;
+ }
+@@ -158,10 +271,17 @@ sub checksum {
+ }
+ my $path = $self->file_path;
+ my $this_checksum;
++ my $this_data;
++ my $this_endata = read_file($path);
++ if ($Wallet::Config::LDAP_SECRET) {
++ $this_data = $self->_file_decrypt($this_endata, $user, $host, $time)
++ } else {
++ $this_data = $this_endata;
++ }
+ if (defined (&Wallet::Config::file_checksum)) {
+- $this_checksum = Wallet::Config::file_checksum($path);
++ $this_checksum = Wallet::Config::file_checksum($this_data);
+ } else {
+- $this_checksum = md5_hex(read_file($path));
++ $this_checksum = md5_hex($this_data);
+ }
+ $self->log_action ('checksum', $user, $host, $time);
+ return $this_checksum;
+@@ -189,6 +309,9 @@ sub store {
+ $self->error ("cannot store $id: $!");
+ return;
+ }
++ if ($Wallet::Config::LDAP_SECRET) {
++ $data = $self->_file_encrypt($data);
++ }
+ unless (print FILE ($data) and close FILE) {
+ $self->error ("cannot store $id: $!");
+ close FILE;
diff --git a/debian/patches/0007-class-error.patch b/debian/patches/0007-class-error.patch
new file mode 100644
index 0000000..b62009c
--- /dev/null
+++ b/debian/patches/0007-class-error.patch
@@ -0,0 +1,30 @@
+Patch to make errors invoking a class more descriptive. The errors
+only happen when there is a reference to a module that does not exist
+or has not been installed.
+
+diff --git a/perl/lib/Wallet/Server.pm b/perl/lib/Wallet/Server.pm
+index b0c955c..f4f9f81 100644
+--- a/perl/lib/Wallet/Server.pm
++++ b/perl/lib/Wallet/Server.pm
+@@ -103,7 +103,7 @@ sub type_mapping {
+ if (defined $class) {
+ eval "require $class";
+ if ($@) {
+- $self->error ($@);
++ print ($self->error ($@));
+ return;
+ }
+ }
+@@ -508,10 +508,10 @@ sub checksum {
+ }
+ my $object = $self->retrieve ($type, $name);
+ if (!defined $object) {
+- return;
++ return;
+ }
+ if (!$self->acl_verify ($object, 'get')) {
+- return;
++ return;
+ }
+ my $result = $object->checksum($self->{user}, $self->{host});
+ $self->error ($object->error) unless defined $result;
diff --git a/debian/patches/0008-invalid-ldap-url.patch b/debian/patches/0008-invalid-ldap-url.patch
new file mode 100644
index 0000000..9ed5ca1
--- /dev/null
+++ b/debian/patches/0008-invalid-ldap-url.patch
@@ -0,0 +1,17 @@
+Make sure that an invalid url error generates output as well as
+stopping processing.
+
+diff --git a/perl/lib/Wallet/Object/File.pm b/perl/lib/Wallet/Object/File.pm
+index e892589..e676759 100644
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -118,8 +118,7 @@ sub _get_crypt_key {
+ my $url = $Wallet::Config::LDAP_SECRET;
+ $url =~ s{^ldap:///}{}xmsi;
+ if ($url eq $Wallet::Config::LDAP_SECRET) {
+- self->error("ERROR: Invalid LDAP URL $url");
+- exit 1;
++ die("ERROR: Invalid LDAP URL $url");
+ }
+ my @parts = split /\?/, $url;
+ my $base = $parts[0];
diff --git a/debian/patches/0009-require-perl-gssapi.patch b/debian/patches/0009-require-perl-gssapi.patch
new file mode 100644
index 0000000..64ff575
--- /dev/null
+++ b/debian/patches/0009-require-perl-gssapi.patch
@@ -0,0 +1,17 @@
+If the GSSAPI module is not present on the wallet server LDAP searches
+will silently perform anonymous binds to the server. This change means
+that when GSSAPI is not present the wallet server will fail when LDAP
+lookups are attempted.
+
+diff --git a/perl/lib/Wallet/ACL/LDAP/Attribute.pm b/perl/lib/Wallet/ACL/LDAP/Attribute.pm
+index 65e0208..02948e2 100644
+--- a/perl/lib/Wallet/ACL/LDAP/Attribute.pm
++++ b/perl/lib/Wallet/ACL/LDAP/Attribute.pm
+@@ -18,6 +18,7 @@ use strict;
+ use warnings;
+
+ use Authen::SASL;
++use GSSAPI;
+ use Net::LDAP qw(LDAP_COMPARE_TRUE);
+ use Wallet::ACL::Base;
+ use Wallet::Config;
diff --git a/debian/patches/0010-empty-store.patch b/debian/patches/0010-empty-store.patch
new file mode 100644
index 0000000..181dfd5
--- /dev/null
+++ b/debian/patches/0010-empty-store.patch
@@ -0,0 +1,110 @@
+Armor encrypt store, syntax fixes
+
+* Encypt the data before attempting any of the actual storage of
+ file objects.
+* Die immediately on any errors retrieving the encryption secret.
+* Correct reference to LDAP_SECRET.
+* Correct arguments passed to _file_decrypt.
+
+diff --git a/perl/lib/Wallet/Object/File.pm b/perl/lib/Wallet/Object/File.pm
+index e676759..d49eef8 100644
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -145,23 +145,29 @@ sub _get_crypt_key {
+ die "ERROR: No entry found for $url";
+ }
+ };
+- if ($@ || !$entry) {
+- die "ERROR: LDAP search failed for $url";
++ if ($@) {
++ die "INFO: LDAP search failed using $url\n"
++ . "ERROR: $@";
++ }
++ if (!$entry) {
++ die "ERROR: No entry returned for LDAP search using $url";
+ }
+
+ my $return_val;
+ my $cnt = 0;
+ foreach my $return_attr ($entry->attributes) {
+- if (lc($return_attr) eq lc($attr)) {
+- $return_val = $entry->get_value($return_val);
++ if ($return_attr =~ /^$attr$/xmsi) {
++ $return_val = $entry->get_value($attr);
++ last;
+ $cnt++;
+ }
+ }
+ if (!$return_val) {
+- die "ERROR: LDAP search failed for $url";
++ die "ERROR: Attribute not found $url";
+ }
+- if ($cnt !=1) {
+- die "ERROR: LDAP search return too many values ($url)";
++ if ($cnt > 0) {
++ my $obj_cnt = $cnt + 1;
++ die "ERROR: LDAP search return too many values ($obj_cnt) for $url";
+ }
+ return $return_val;
+ }
+@@ -185,7 +191,9 @@ sub _file_crypt {
+ if ($action eq 'encrypt') {
+ $return_string = $pre . encode_base64($cipher->encrypt($string));
+ } elsif ($action eq 'decrypt') {
+- if ($string =~ s/^$pre//xms) {
++ my $pre_regex = $pre;
++ $pre_regex =~ s/(\W)/\\$1/g;
++ if ($string =~ s/^$pre_regex//xms) {
+ $return_string = $cipher->decrypt(decode_base64($string));
+ } else {
+ $return_string = $string;
+@@ -193,8 +201,7 @@ sub _file_crypt {
+ } else {
+ my $msg = "ERROR: invalid action ($action)\n ";
+ $msg .= "INFO: action must be 'encrypt' or 'decrypt'\n";
+- $self->error($msg);
+- return;
++ die $msg;
+ }
+ return $return_string;
+ }
+@@ -203,7 +210,7 @@ sub _file_decrypt {
+ my ($self, $data, $user, $host, $time) = @_;
+ my $undata = $self->_file_crypt('decrypt', $data);
+ if ($undata eq $data) {
+- $self->store($data, $user, $host, $time) = @_;
++ $self->store($data, $user, $host, $time);
+ }
+ return $undata;
+ }
+@@ -251,8 +258,8 @@ sub get {
+ $self->error ("cannot get $id: $!");
+ return;
+ }
+- if ($Wallet::LDAP::SECRET) {
+- $data = self->_file_decrypt($user, $host, $time, $data);
++ if ($Wallet::Config::LDAP_SECRET) {
++ $data = $self->_file_decrypt($data, $user, $host, $time);
+ }
+ $self->log_action ('get', $user, $host, $time);
+ return $data;
+@@ -301,15 +308,16 @@ sub store {
+ return;
+ }
+ }
++ if ($Wallet::Config::LDAP_SECRET) {
++ $data = $self->_file_encrypt($data);
++ }
++
+ my $path = $self->file_path;
+ return unless $path;
+ unless (open (FILE, '>', $path)) {
+ $self->error ("cannot store $id: $!");
+ return;
+ }
+- if ($Wallet::Config::LDAP_SECRET) {
+- $data = $self->_file_encrypt($data);
+- }
+ unless (print FILE ($data) and close FILE) {
+ $self->error ("cannot store $id: $!");
+ close FILE;
diff --git a/debian/patches/0011-doc.patch b/debian/patches/0011-doc.patch
new file mode 100644
index 0000000..3f4e41f
--- /dev/null
+++ b/debian/patches/0011-doc.patch
@@ -0,0 +1,168 @@
+Minor changes to the documentation
+
+Wallet client man page
+* Add the checksum command
+* Add acl add examples
+
+Update documentation for Object encryption in Config.pm
+--- a/client/wallet.pod
++++ b/client/wallet.pod
+@@ -181,7 +181,13 @@ For more information on attributes, see
+ =item acl add <id> <scheme> <identifier>
+
+ Add an entry with <scheme> and <identifier> to the ACL <id>. <id> may be
+-either the name of an ACL or its numeric identifier.
++either the name of an ACL or its numeric identifier. Three schemes are
++supported: krb5, netdb, and ldap-attr. The netdb and ldap-attr must
++be configured before they can be used. Examples:
++
++ wallet acl add config/db krb5 mac@CA-ZEPHYR.ORG
++ wallet acl add config/db netdb host/keddie.ca-zephyr.org
++ wallet acl add config/db ldap-attr czPrivilegeGroup=admin
+
+ =item acl check <id>
+
+@@ -257,6 +263,10 @@ already exist.
+ Check whether an object of type <type> and name <name> already exists. If
+ it does, prints C<yes>; if not, prints C<no>.
+
++=item checksum
++
++Returns the checksum for file objects.
++
+ =item comment <type> <name> [<comment>]
+
+ If <comment> is not given, displays the current comment for the object
+@@ -390,7 +400,7 @@ will attempt to automatically create it
+
+ 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
++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.
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -723,6 +723,9 @@ specify the LDAP server and additional c
+ information required for the wallet to check for the existence of
+ attributes.
+
++The format for specifying an LDAP ACL is "acl-attr "<simple filter>"
++where a simple filter is of the form "attribute=value".
++
+ =over 4
+
+ =item LDAP_HOST
+@@ -765,35 +768,6 @@ with this ACL type. This variable must
+
+ =cut
+
+-our $LDAP_SECRET;
+-
+-=item LDAP_SECRET
+-
+-Specifies an LDAP URL that is used to retrieve the secret to use when
+-encrypting and decrypting file objects. The url must not include the
+-hostname. LDAP_HOST will be used as the hostname to bind to. The
+-Kerberos ticket cache specified in LDAP_CACHE is used when connecting
+-to the LDAP server. GSS-API authentication is always used; there is
+-currently no support for any other type of bind. The ticket cache
+-must be for a principal with access to retrieve the secret. This
+-variable and LDAP_CACHE must be set to use file object encryption.
+-
+-=cut
+-
+-our $LDAP_SECRET_PREFIX;
+-
+-=item LDAP_SECRET_PREFIX
+-
+-Specifies the prefix to be used when generating storing an encrypted
+-file object. The prefix is used to determine whether or not a file
+-object has been stored encrypted. This allows the gradual transition
+-from unencrypted file objects to encrypted file objects. When file
+-object encryption is enable any "get" of an unencyrpted file object
+-will result in the replacement of the unencrypted object with an
+-encrypted object.
+-
+-=cut
+-
+ our $LDAP_CACHE;
+
+ =back
+@@ -825,13 +799,48 @@ Note that this example only removes the
+ Any principal from some other realm will be left fully qualified, and then
+ presumably will not be found in the directory.
+
+-=head2 File Object Encryption
++=head1 FILE OBJECT ENCRYPTION
++
++=over 4
++
++=item LDAP_SECRET
++
++Specifies an LDAP URL that is used to retrieve the secret to use when
++encrypting and decrypting file objects. The url must not include the
++hostname. LDAP_HOST will be used as the hostname to bind to. The
++Kerberos ticket cache specified in LDAP_CACHE is used when connecting
++to the LDAP server. GSS-API authentication is always used; there is
++currently no support for any other type of bind. The ticket cache
++must be for a principal with access to retrieve the secret. This
++variable and LDAP_CACHE must be set to use file object encryption.
++
++=cut
++
++our $LDAP_SECRET;
++
++=item LDAP_SECRET_PREFIX
+
+-The default encryption method use is based on the twofish cypher. If
++Specifies the prefix to be used when generating storing an encrypted
++file object. The prefix is used to determine whether or not a file
++object has been stored encrypted. This allows the gradual transition
++from unencrypted file objects to encrypted file objects. When file
++object encryption is enable any "get" of an unencyrpted file object
++will result in the replacement of the unencrypted object with an
++encrypted object.
++
++=cut
++
++our $LDAP_SECRET_PREFIX;
++
++=item file_crypt;
++
++This functionality has not been implmented yet.
++
++The default encryption method is based on the twofish cypher. If
+ another encryption method is desired then the perl function file_crypt
+-should be defined. The function must accept three parameters: the
+-action to preform, the encryption secret, and the string to encrypt or
+-decrypt. For example:
++should be defined in the configuration file. The function must accept
++three parameters: the action to preform, the encryption secret, and
++the string to encrypt or decrypt. For example:
+
+ sub file_crypt {
+ use Crypt::RC4;
+@@ -846,6 +855,21 @@ decrypt. For example:
+ return $return_string;
+ }
+
++=item file_crypt_secret
++
++This functionality has not been implmented yet.
++
++The default method use is based on the twofish cypher. If another
++method of retrieving a secret is desired then the perl function
++file_crypt_secret should be defined. The function accepts no
++parameters and returns the secret to be used. For example:
++
++ sub file_crypt_secret {
++ return "thisIsABadIdea";
++ }
++
++=back
++
+ =head1 NETDB ACL CONFIGURATION
+
+ These configuration variables are only needed if you intend to use the
diff --git a/debian/patches/0012-pwd-options.patch b/debian/patches/0012-pwd-options.patch
new file mode 100644
index 0000000..659112b
--- /dev/null
+++ b/debian/patches/0012-pwd-options.patch
@@ -0,0 +1,130 @@
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -266,6 +266,27 @@ that is inherited.
+
+ =over 4
+
++=item PWD_TYPE
++
++Specifies the algorithm to use when generating a password. The
++default is RANDOM which specified that random set of printable ASCII
++characters will be used to create a password. The pool of characters
++can be restricted by specifying PWD_CHARACTERS.
++
++The PWD_TYPE can be used to specify the generation of passwords using
++the XKCD inspired perl module Crypt::HSXKPasswd. Three values are
++supported that use preset configuration values: XKCD-DEFAULT,
++XKCD-APPLIED, and XKCD. See the module documentation for the details
++of these presents.
++
++A PWD_TYPE of CUSTOM is supported. If defined then the configuration
++file must contain a Perl function generate_password that returns a
++generated password.
++
++=cut
++
++our $PWD_TYPE = 'RANDOM';
++
+ =item PWD_FILE_BUCKET
+
+ The directory into which to store password objects. Password objects will
+@@ -274,7 +295,7 @@ L<Wallet::Object::Password> for the full
+ directory must be writable by the wallet server and the wallet server must
+ be able to create subdirectories of it.
+
+-PWD_FILE_BUCKET must be set to use file objects.
++PWD_FILE_BUCKET must be set to use password objects.
+
+ =cut
+
+--- a/perl/lib/Wallet/Object/Password.pm
++++ b/perl/lib/Wallet/Object/Password.pm
+@@ -52,6 +52,38 @@ sub file_path {
+ return "$Wallet::Config::PWD_FILE_BUCKET/$hash/$name";
+ }
+
++# Returns a password of random characters.
++sub _pwd_random {
++ my ($self) = @_;
++ my $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
++ $Wallet::Config::PWD_LENGTH_MAX);
++ if ($Wallet::Config::PWD_CHARACTERS) {
++ my @pw_chars = ();
++ for (my $i=0; $i<length($Wallet::Config::PWD_CHARACTERS); $i++) {
++ push @pw_chars, substr($Wallet::Config::PWD_CHARACTERS, $i, 1);
++ }
++ $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
++ $Wallet::Config::PWD_LENGTH_MAX,
++ \@pw_chars);
++ }
++ return $pass;
++}
++
++# Returns a password using Crypt::HSXKPasswd
++sub _pwd_xkcd {
++ my ($self, $preset) = @_;
++
++ if (! $preset) {
++ $preset = 'XKCD';
++ } else {
++ $preset =~ s/^\-//xms;
++ }
++ require Crypt::HSXKPasswd;
++ my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(preset => $preset);
++ my $pass = $hsxkpasswd_instance->password();
++ return $pass;
++}
++
+ ##############################################################################
+ # Shared methods
+ ##############################################################################
+@@ -75,20 +107,37 @@ sub retrieve {
+ ob_name => $self->{name});
+ my $object = $schema->resultset('Object')->find (\%search);
+ if (!$object->ob_stored_on || $operation eq 'update') {
+- unless (open (FILE, '>', $path)) {
+- $self->error ("cannot store initial settings for $id: $!\n");
+- return;
++ my $pass;
++ if ($Wallet::Config::PWD_TYPE eq 'RANDOM')
++ {
++ $pass = $self->_pwd_random();
++ }
++ elsif ($Wallet::Config::PWD_TYPE
++ =~ /^XKCD(\-APPLIED|\-DEFAULT){0,1}/xms)
++ {
++ $pass = $self->_pwd_xkcd($1);
++ }
++ elsif ($Wallet::Config::PWD_TYPE eq 'CUSTOM')
++ {
++ if (defined(&Wallet::Config::generate_password)) {
++ $pass = Wallet::Config::generate_password();
++ } else {
++ $self->error ("function generate_password() not found\n");
++ return;
++ }
+ }
+- my $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
+- $Wallet::Config::PWD_LENGTH_MAX);
+- if ($Wallet::Config::PWD_CHARACTERS) {
+- my @pw_chars = ();
+- for (my $i=0; $i<length($Wallet::Config::PWD_CHARACTERS); $i++) {
+- push @pw_chars, substr($Wallet::Config::PWD_CHARACTERS, $i, 1);
++ else
++ {
++ if (defined($Wallet::Config::PWD_TYPE)) {
++ $self->error ("Unknown PWD_TYPE ($Wallet::Config::PWD_TYPE)\n");
++ } else {
++ $self->error ("PWD_TYPE not set\n");
+ }
+- $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
+- $Wallet::Config::PWD_LENGTH_MAX,
+- \@pw_chars);
++ return;
++ }
++ unless (open (FILE, '>', $path)) {
++ $self->error ("cannot open $path $!\n");
++ return;
+ }
+ print FILE $pass;
+ $self->log_action ('store', $user, $host, $time);
diff --git a/debian/patches/0013-crypt-fixup.patch b/debian/patches/0013-crypt-fixup.patch
new file mode 100644
index 0000000..8d0ed99
--- /dev/null
+++ b/debian/patches/0013-crypt-fixup.patch
@@ -0,0 +1,117 @@
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -175,8 +175,8 @@ sub _get_crypt_key {
+ sub _file_crypt {
+ my ($self, $action, $string) = @_;
+
+- use Crypt::CBC;
+- use MIME::Base64;
++ require Crypt::CBC;
++ require MIME::Base64;
+
+ my $return_string;
+ my $pre = $Wallet::Config::LDAP_SECRET_PREFIX;
+@@ -189,12 +189,14 @@ sub _file_crypt {
+ -add_header => 1
+ );
+ if ($action eq 'encrypt') {
+- $return_string = $pre . encode_base64($cipher->encrypt($string));
++ $return_string
++ = $pre . MIME::Base64::encode_base64($cipher->encrypt($string));
+ } elsif ($action eq 'decrypt') {
+ my $pre_regex = $pre;
+ $pre_regex =~ s/(\W)/\\$1/g;
+ if ($string =~ s/^$pre_regex//xms) {
+- $return_string = $cipher->decrypt(decode_base64($string));
++ $return_string
++ = $cipher->decrypt(MIME::Base64::decode_base64($string));
+ } else {
+ $return_string = $string;
+ }
+@@ -206,7 +208,7 @@ sub _file_crypt {
+ return $return_string;
+ }
+
+-sub _file_decrypt {
++sub file_decrypt {
+ my ($self, $data, $user, $host, $time) = @_;
+ my $undata = $self->_file_crypt('decrypt', $data);
+ if ($undata eq $data) {
+@@ -215,7 +217,7 @@ sub _file_decrypt {
+ return $undata;
+ }
+
+-sub _file_encrypt {
++sub file_encrypt {
+ my ($self, $data) = @_;
+ my $endata = $self->_file_crypt('encrypt', $data);
+ return $endata;
+@@ -259,7 +261,7 @@ sub get {
+ return;
+ }
+ if ($Wallet::Config::LDAP_SECRET) {
+- $data = $self->_file_decrypt($data, $user, $host, $time);
++ $data = $self->file_decrypt($data, $user, $host, $time);
+ }
+ $self->log_action ('get', $user, $host, $time);
+ return $data;
+@@ -279,7 +281,7 @@ sub checksum {
+ my $this_data;
+ my $this_endata = read_file($path);
+ if ($Wallet::Config::LDAP_SECRET) {
+- $this_data = $self->_file_decrypt($this_endata, $user, $host, $time)
++ $this_data = $self->file_decrypt($this_endata, $user, $host, $time)
+ } else {
+ $this_data = $this_endata;
+ }
+@@ -309,7 +311,7 @@ sub store {
+ }
+ }
+ if ($Wallet::Config::LDAP_SECRET) {
+- $data = $self->_file_encrypt($data);
++ $data = $self->file_encrypt($data);
+ }
+
+ my $path = $self->file_path;
+--- a/perl/lib/Wallet/Object/Password.pm
++++ b/perl/lib/Wallet/Object/Password.pm
+@@ -122,16 +122,16 @@ sub retrieve {
+ if (defined(&Wallet::Config::generate_password)) {
+ $pass = Wallet::Config::generate_password();
+ } else {
+- $self->error ("function generate_password() not found\n");
++ $self->error("function generate_password() not found\n");
+ return;
+ }
+ }
+ else
+ {
+ if (defined($Wallet::Config::PWD_TYPE)) {
+- $self->error ("Unknown PWD_TYPE ($Wallet::Config::PWD_TYPE)\n");
++ $self->error("Unknown PWD_TYPE ($Wallet::Config::PWD_TYPE)\n");
+ } else {
+- $self->error ("PWD_TYPE not set\n");
++ $self->error("PWD_TYPE not set\n");
+ }
+ return;
+ }
+@@ -139,6 +139,9 @@ sub retrieve {
+ $self->error ("cannot open $path $!\n");
+ return;
+ }
++ if ($Wallet::Config::LDAP_SECRET) {
++ $pass = Wallet::Object::File->file_encrypt($pass);
++ }
+ print FILE $pass;
+ $self->log_action ('store', $user, $host, $time);
+ unless (close FILE) {
+@@ -158,6 +161,9 @@ sub retrieve {
+ return;
+ }
+ $self->log_action ($operation, $user, $host, $time);
++ if ($Wallet::Config::LDAP_SECRET) {
++ $data = Wallet::Object::File->file_decrypt($data);
++ }
+ return $data;
+ }
+
diff --git a/debian/patches/0014-crypt-custom.patch b/debian/patches/0014-crypt-custom.patch
new file mode 100644
index 0000000..e900ed8
--- /dev/null
+++ b/debian/patches/0014-crypt-custom.patch
@@ -0,0 +1,210 @@
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -791,39 +791,6 @@ with this ACL type. This variable must
+
+ our $LDAP_CACHE;
+
+-=back
+-
+-=head2 LDAP Principal Mapping
+-
+-Depending on the structure of the LDAP directory being queried,
+-there may not be any attribute in the directory whose value exactly
+-matches the Kerberos principal. The attribute designated by
+-LDAP_FILTER_ATTR may instead hold a transformation of the principal name
+-(such as the principal with the local realm stripped off, or rewritten
+-into an LDAP DN form). If this is the case, define a Perl function named
+-ldap_map_principal. This function will be called whenever an LDAP
+-attribute ACL is being verified. It will take one argument, the
+-principal, and is expected to return the value to search for in the LDAP
+-directory server.
+-
+-For example, if the principal name without the local realm is stored in
+-the C<uid> attribute in the directory, set LDAP_FILTER_ATTR to C<uid> and
+-then define ldap_map_attribute as follows:
+-
+- sub ldap_map_principal {
+- my ($principal) = @_;
+- $principal =~ s/\@EXAMPLE\.COM$//;
+- return $principal;
+- }
+-
+-Note that this example only removes the local realm (here, EXAMPLE.COM).
+-Any principal from some other realm will be left fully qualified, and then
+-presumably will not be found in the directory.
+-
+-=head1 FILE OBJECT ENCRYPTION
+-
+-=over 4
+-
+ =item LDAP_SECRET
+
+ Specifies an LDAP URL that is used to retrieve the secret to use when
+@@ -853,43 +820,34 @@ encrypted object.
+
+ our $LDAP_SECRET_PREFIX;
+
+-=item file_crypt;
+-
+-This functionality has not been implmented yet.
+-
+-The default encryption method is based on the twofish cypher. If
+-another encryption method is desired then the perl function file_crypt
+-should be defined in the configuration file. The function must accept
+-three parameters: the action to preform, the encryption secret, and
+-the string to encrypt or decrypt. For example:
+-
+- sub file_crypt {
+- use Crypt::RC4;
+- my ($action, $secret, $string) = @_;
+-
+- my $return_string;
+- if ($action eq 'encrypt') {
+- $return_string = RC4($secret, $string);
+- } elsif ($action eq 'decrypt') {
+- $return_string = RC4($secret, $string);
+- }
+- return $return_string;
+- }
++=back
+
+-=item file_crypt_secret
++=head2 LDAP Principal Mapping
+
+-This functionality has not been implmented yet.
++Depending on the structure of the LDAP directory being queried,
++there may not be any attribute in the directory whose value exactly
++matches the Kerberos principal. The attribute designated by
++LDAP_FILTER_ATTR may instead hold a transformation of the principal name
++(such as the principal with the local realm stripped off, or rewritten
++into an LDAP DN form). If this is the case, define a Perl function named
++ldap_map_principal. This function will be called whenever an LDAP
++attribute ACL is being verified. It will take one argument, the
++principal, and is expected to return the value to search for in the LDAP
++directory server.
+
+-The default method use is based on the twofish cypher. If another
+-method of retrieving a secret is desired then the perl function
+-file_crypt_secret should be defined. The function accepts no
+-parameters and returns the secret to be used. For example:
++For example, if the principal name without the local realm is stored in
++the C<uid> attribute in the directory, set LDAP_FILTER_ATTR to C<uid> and
++then define ldap_map_attribute as follows:
+
+- sub file_crypt_secret {
+- return "thisIsABadIdea";
++ sub ldap_map_principal {
++ my ($principal) = @_;
++ $principal =~ s/\@EXAMPLE\.COM$//;
++ return $principal;
+ }
+
+-=back
++Note that this example only removes the local realm (here, EXAMPLE.COM).
++Any principal from some other realm will be left fully qualified, and then
++presumably will not be found in the directory.
+
+ =head1 NETDB ACL CONFIGURATION
+
+@@ -1154,6 +1112,41 @@ as a base64 string.
+ return $cs;
+ }
+
++=head1 ENCRYPTION METHODS
++
++The default encryption method is based on the twofish cypher. If
++another encryption method is desired then the perl function file_crypt
++should be defined in the configuration file. The function must accept
++three parameters: the action to preform, the encryption secret, and
++the string to encrypt or decrypt. For example:
++
++ sub file_crypt {
++ my ($action, $secret, $string) = @_;
++
++ my $cipher = Crypt::CBC->new(-key => $secret,
++ -cipher => 'Blowfish');
++
++ my $return_string;
++ if ($action eq 'encrypt') {
++ $return_string = $cipher->encrypt($string);
++ } elsif ($action eq 'decrypt') {
++ $return_string = $cipher->decrypt($string);
++ } else {
++ print("Unknown encryption action ($action)\n");
++ }
++ return $return_string;
++ }
++
++The default method for retrieving the secret used to encryption
++operations is retrieved from an LDAP server. If another method of
++retrieving a secret is desired then the perl function
++file_crypt_secret should be defined. The function accepts no
++parameters and returns the secret to be used. For example:
++
++ sub file_crypt_secret {
++ return "thisIsABadIdea";
++ }
++
+ =head1 ENVIRONMENT
+
+ =over 4
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -114,6 +114,11 @@ sub rename {
+ sub _get_crypt_key {
+ my ($self) = @_;
+
++ if (defined (&Wallet::Config::file_crypt_secret)) {
++ my $return_val = Wallet::Config::file_crypt_secret();
++ return $return_val;
++ }
++
+ # ldap:///basedn?attr?scope?filter
+ my $url = $Wallet::Config::LDAP_SECRET;
+ $url =~ s{^ldap:///}{}xmsi;
+@@ -173,14 +178,13 @@ sub _get_crypt_key {
+ }
+
+ sub _file_crypt {
+- my ($self, $action, $string) = @_;
++ my ($self, $action, $key, $string) = @_;
+
+ require Crypt::CBC;
+ require MIME::Base64;
+
+ my $return_string;
+ my $pre = $Wallet::Config::LDAP_SECRET_PREFIX;
+- my $key = $self->_get_crypt_key();
+
+ my $cipher = Crypt::CBC->new(
+ -key => $key,
+@@ -210,7 +214,13 @@ sub _file_crypt {
+
+ sub file_decrypt {
+ my ($self, $data, $user, $host, $time) = @_;
+- my $undata = $self->_file_crypt('decrypt', $data);
++ my $key = $self->_get_crypt_key();
++ my $undata;
++ if (defined (&Wallet::Config::file_crypt)) {
++ $undata = Wallet::Config::file_crypt('decrypt', $key, $data);
++ } else {
++ $undata = $self->_file_crypt('decrypt', $key, $data);
++ }
+ if ($undata eq $data) {
+ $self->store($data, $user, $host, $time);
+ }
+@@ -219,7 +229,13 @@ sub file_decrypt {
+
+ sub file_encrypt {
+ my ($self, $data) = @_;
+- my $endata = $self->_file_crypt('encrypt', $data);
++ my $key = $self->_get_crypt_key();
++ my $endata;
++ if (defined (&Wallet::Config::file_crypt)) {
++ $endata = Wallet::Config::file_crypt('encrypt', $key, $data);
++ } else {
++ $endata = $self->_file_crypt('encrypt', $key, $data);
++ }
+ return $endata;
+ }
+
diff --git a/debian/patches/0015-encryption-fixes.patch b/debian/patches/0015-encryption-fixes.patch
new file mode 100644
index 0000000..3fc7c82
--- /dev/null
+++ b/debian/patches/0015-encryption-fixes.patch
@@ -0,0 +1,227 @@
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -119,17 +119,44 @@ sub _get_crypt_key {
+ return $return_val;
+ }
+
++ my $msg = "\n";
++
+ # ldap:///basedn?attr?scope?filter
+ my $url = $Wallet::Config::LDAP_SECRET;
+ $url =~ s{^ldap:///}{}xmsi;
+ if ($url eq $Wallet::Config::LDAP_SECRET) {
+- die("ERROR: Invalid LDAP URL $url");
++ $msg .= "INFO: LDAP_SECRET contains a hostname\n";
++ $msg .= "ERROR: Invalid LDAP_SECRET URL $url\n";
++ die $msg;
+ }
+ my @parts = split /\?/, $url;
+ my $base = $parts[0];
++ if (!$base) {
++ $msg .= "INFO: LDAP_SECRET does not contain a base dn\n";
++ $msg .= "ERROR: Invalid LDAP_SECRET URL $url\n";
++ die $msg;
++ }
+ my $attr = $parts[1];
++ if (!$attr) {
++ $msg .= "INFO: LDAP_SECRET does not contain an attribute\n";
++ $msg .= "ERROR: Invalid LDAP_SECRET URL $url\n";
++ die $msg;
++ }
+ my $scope = $parts[2];
++ if (!$scope) {
++ $scope = 'subtree';
++ }
+ my $filter = $parts[3];
++ if (!$filter) {
++ $filter = 'objectClass=*';
++ }
++
++ # Search details just in case they have to be displayed
++ my $search_details = "\n";
++ $search_details .= "INFO: base = $base\n";
++ $search_details .= "INFO: filter = $filter\n";
++ $search_details .= "INFO: scope = $scope\n";
++ $search_details .= "INFO: attribute = $attr\n";
+
+ # Search for the secret in the LDAP directory
+ my $ldap_obj = Wallet::ACL::LDAP::Attribute->new;
+@@ -145,17 +172,23 @@ sub _get_crypt_key {
+ if ($search->count == 1) {
+ $entry = $search->pop_entry;
+ } elsif ($search->count > 1) {
+- die 'ERROR: ' . $search->count . " LDAP entries found for $filter";
++ $msg .= 'ERROR: ' . $search->count . ' LDAP entries found for '
++ . "$filter\n";
++ die $msg;
+ } else {
+- die "ERROR: No entry found for $url";
++ my $msg .= $search_details;
++ $msg .= "ERROR: No entry found for $url\n";
++ die $msg;
+ }
+ };
+ if ($@) {
+- die "INFO: LDAP search failed using $url\n"
+- . "ERROR: $@";
++ $msg .= "ERROR: LDAP search failed using $url\n";
++ $msg .= "ERROR: $@" . "\n";
++ die $msg;
+ }
+ if (!$entry) {
+- die "ERROR: No entry returned for LDAP search using $url";
++ $msg .= "ERROR: No entry returned for LDAP search using $url";
++ die $msg;
+ }
+
+ my $return_val;
+@@ -168,11 +201,15 @@ sub _get_crypt_key {
+ }
+ }
+ if (!$return_val) {
+- die "ERROR: Attribute not found $url";
++ $msg .= $search_details;
++ $msg .= "ERROR: Attribute not found $url\n";
++ die $msg;
+ }
+ if ($cnt > 0) {
+ my $obj_cnt = $cnt + 1;
+- die "ERROR: LDAP search return too many values ($obj_cnt) for $url";
++ $msg .= $search_details;
++ $msg .= "ERROR: LDAP returned too many values ($obj_cnt) for $url";
++ die $msg;
+ }
+ return $return_val;
+ }
+@@ -184,7 +221,7 @@ sub _file_crypt {
+ require MIME::Base64;
+
+ my $return_string;
+- my $pre = $Wallet::Config::LDAP_SECRET_PREFIX;
++ my $pre = $Wallet::Config::ENCRYPTION_PREFIX;
+
+ my $cipher = Crypt::CBC->new(
+ -key => $key,
+@@ -219,6 +256,12 @@ sub file_decrypt {
+ if (defined (&Wallet::Config::file_crypt)) {
+ $undata = Wallet::Config::file_crypt('decrypt', $key, $data);
+ } else {
++ if (!$Wallet::Config::LDAP_SECRET) {
++ my $msg = "\n";
++ $msg .= "ERROR: ENCYPTION_PREFIX specified and LDAP_SECRET is "
++ . "missing\n";
++ die $msg;
++ }
+ $undata = $self->_file_crypt('decrypt', $key, $data);
+ }
+ if ($undata eq $data) {
+@@ -234,6 +277,12 @@ sub file_encrypt {
+ if (defined (&Wallet::Config::file_crypt)) {
+ $endata = Wallet::Config::file_crypt('encrypt', $key, $data);
+ } else {
++ if (!$Wallet::Config::LDAP_SECRET) {
++ my $msg = "\n";
++ $msg .= "ERROR: ENCYPTION_PREFIX specified and LDAP_SECRET is "
++ . "missing\n";
++ die $msg;
++ }
+ $endata = $self->_file_crypt('encrypt', $key, $data);
+ }
+ return $endata;
+@@ -276,7 +325,7 @@ sub get {
+ $self->error ("cannot get $id: $!");
+ return;
+ }
+- if ($Wallet::Config::LDAP_SECRET) {
++ if ($Wallet::Config::ENCRYPTION_PREFIX) {
+ $data = $self->file_decrypt($data, $user, $host, $time);
+ }
+ $self->log_action ('get', $user, $host, $time);
+@@ -296,7 +345,7 @@ sub checksum {
+ my $this_checksum;
+ my $this_data;
+ my $this_endata = read_file($path);
+- if ($Wallet::Config::LDAP_SECRET) {
++ if ($Wallet::Config::ENCRYPTION_PREFIX) {
+ $this_data = $self->file_decrypt($this_endata, $user, $host, $time)
+ } else {
+ $this_data = $this_endata;
+@@ -326,7 +375,7 @@ sub store {
+ return;
+ }
+ }
+- if ($Wallet::Config::LDAP_SECRET) {
++ if ($Wallet::Config::ENCRYPTION_PREFIX) {
+ $data = $self->file_encrypt($data);
+ }
+
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -795,31 +795,19 @@ our $LDAP_CACHE;
+
+ Specifies an LDAP URL that is used to retrieve the secret to use when
+ encrypting and decrypting file objects. The url must not include the
+-hostname. LDAP_HOST will be used as the hostname to bind to. The
+-Kerberos ticket cache specified in LDAP_CACHE is used when connecting
+-to the LDAP server. GSS-API authentication is always used; there is
+-currently no support for any other type of bind. The ticket cache
+-must be for a principal with access to retrieve the secret. This
+-variable and LDAP_CACHE must be set to use file object encryption.
++hostname. LDAP_HOST will be used as the hostname for the server to
++bind to. The Kerberos ticket cache specified in LDAP_CACHE is used
++when connecting to the LDAP server. GSS-API authentication is always
++used; there is currently no support for any other type of bind. The
++ticket cache must be for a principal with access to retrieve the
++secret. The variables LDAP_HOST, LDAP_CACHE, LDAP_SECRET, and
++ENCRYPTION_PREFIX must be set to use the default encryption
++processing.
+
+ =cut
+
+ our $LDAP_SECRET;
+
+-=item LDAP_SECRET_PREFIX
+-
+-Specifies the prefix to be used when generating storing an encrypted
+-file object. The prefix is used to determine whether or not a file
+-object has been stored encrypted. This allows the gradual transition
+-from unencrypted file objects to encrypted file objects. When file
+-object encryption is enable any "get" of an unencyrpted file object
+-will result in the replacement of the unencrypted object with an
+-encrypted object.
+-
+-=cut
+-
+-our $LDAP_SECRET_PREFIX;
+-
+ =back
+
+ =head2 LDAP Principal Mapping
+@@ -1112,7 +1100,26 @@ as a base64 string.
+ return $cs;
+ }
+
+-=head1 ENCRYPTION METHODS
++=head1 ENCRYPTION CONFIGURATION AND METHODS
++
++=over 4
++
++=item ENCRYPTION_PREFIX
++
++Specifies the prefix to be used when generating storing an encrypted
++file object. The prefix is used to determine whether or not a file
++object has been stored encrypted. This allows the gradual transition
++from unencrypted file objects to encrypted file objects. When file
++object encryption is enable any "get" of an unencyrpted file object
++will result in the replacement of the unencrypted object with an
++encrypted object. This variable must be set to use file and password
++encyrption.
++
++=cut
++
++our $ENCRYPTION_PREFIX;
++
++=back
+
+ The default encryption method is based on the twofish cypher. If
+ another encryption method is desired then the perl function file_crypt
diff --git a/debian/patches/0016-checkfile.patch b/debian/patches/0016-checkfile.patch
new file mode 100644
index 0000000..e4aa270
--- /dev/null
+++ b/debian/patches/0016-checkfile.patch
@@ -0,0 +1,841 @@
+--- /dev/null
++++ b/client/checkfile.c
+@@ -0,0 +1,141 @@
++/*
++ * Test file object checksum against a file on the system
++ *
++ * Written by Bill MacAllister <whm@dropbox.com>
++ * Copyright 2020 Dropbox, Inc.
++ *
++ * SPDX-License-Identifier: MIT
++ */
++
++#include <config.h>
++#include <portable/system.h>
++
++#include <errno.h>
++#include <fcntl.h>
++#include <sys/stat.h>
++
++#include <client/internal.h>
++#include <util/messages.h>
++#include <util/xmalloc.h>
++
++#include <stdlib.h>
++#include <string.h>
++#include <openssl/md5.h>
++
++/* Given a string and its length return a checksum for the string.
++ */
++char *checkfile_str2md5(const char *str, int length) {
++ int n;
++ MD5_CTX c;
++ unsigned char digest[16];
++ char *out = (char*)malloc(33);
++
++ MD5_Init(&c);
++
++ while (length > 0) {
++ if (length > 512) {
++ MD5_Update(&c, str, 512);
++ } else {
++ MD5_Update(&c, str, length);
++ }
++ length -= 512;
++ str += 512;
++ }
++
++ MD5_Final(digest, &c);
++
++ for (n = 0; n < 16; ++n) {
++ snprintf(&(out[n*2]), 16*2, "%02x", (unsigned int)digest[n]);
++ }
++
++ return out;
++}
++
++/*
++ * Read all of a file into memory and return the contents in newly allocated
++ * memory. Returns the size of the file contents in the second argument if
++ * it's not NULL. Dies on any failure.
++ */
++void *
++checkfile_read_file(const char *name, size_t *length)
++{
++ char *contents;
++ size_t size, offset;
++ int fd;
++ struct stat st;
++ ssize_t status;
++
++ fd = open(name, O_RDONLY);
++ if (fd < 0)
++ return NULL;
++ if (fstat(fd, &st) < 0)
++ return NULL;
++ size = st.st_size;
++ contents = xmalloc(size);
++
++ offset = 0;
++ do {
++ if (offset >= size - 1) {
++ size += BUFSIZ;
++ contents = xrealloc(contents, size);
++ }
++ do {
++ status = read(fd, contents + offset, size - offset - 1);
++ } while (status == -1 && errno == EINTR);
++ if (status < 0)
++ sysdie("cannot read from file");
++ offset += status;
++ } while (status > 0);
++ close(fd);
++ if (length != NULL)
++ *length = offset;
++ return contents;
++}
++
++/*
++ * Given a remctl object, the command prefix, object type, and object name,
++ * and a file calculate the checksum of the file, get the checksum of
++ * the wallet object, and compare them. Returns 0 if the checksums
++ * match and an exit status on if the don't match.
++ */
++int
++checkfile(struct remctl *r, const char *prefix, const char *type,
++ const char *name, const char *file)
++{
++ const char *command[5];
++ char *file_data = NULL;
++ char *file_checksum = NULL;
++ size_t count, length;
++ char *wallet_checksum = NULL;
++ size_t wallet_length = 0;
++ int status;
++
++ /* Check to see if the file exists. It not return mismatch status */
++ file_data = checkfile_read_file(file, &length);
++ if (file_data == NULL) {
++ return 1;
++ }
++
++ command[0] = prefix;
++ command[1] = "checksum";
++ command[2] = type;
++ command[3] = name;
++ command[4] = NULL;
++ status = run_command(r, command, &wallet_checksum, &wallet_length);
++ if (status != 0) {
++ return status;
++ }
++
++ /* If the wallet object is empty or NULL return mismatch and let the
++ * subsequent call to file handle the error condition of an empty
++ * file object.
++ */
++ if ((wallet_length == 0) || (wallet_checksum == NULL)) {
++ return 1;
++ }
++
++ file_checksum = checkfile_str2md5(file_data, length);
++ status = strncmp(file_checksum, wallet_checksum, wallet_length);
++
++ return status;
++}
+--- a/client/wallet.c
++++ b/client/wallet.c
+@@ -124,9 +124,18 @@ main(int argc, char *argv[])
+ usage(1);
+
+ /* -f is only supported for get and store and -S with get keytab. */
+- if (file != NULL)
+- if (strcmp(argv[0], "get") != 0 && strcmp(argv[0], "store") != 0)
++ if (file != NULL) {
++ if (strcmp(argv[0], "get") != 0 &&
++ strcmp(argv[0], "store") != 0 &&
++ strcmp(argv[0], "checkfile") != 0)
++ {
+ die("-f only supported for get and store");
++ }
++ } else {
++ if (strcmp(argv[0], "checkfile") == 0)
++ die("-f is required by checkfile");
++ }
++
+ if (srvtab != NULL) {
+ if (strcmp(argv[0], "get") != 0 || strcmp(argv[1], "keytab") != 0)
+ die("-S only supported for get keytab");
+@@ -172,6 +181,14 @@ main(int argc, char *argv[])
+ if (argc > 2)
+ die("too many arguments");
+ status = rekey_keytab(r, ctx, options.type, argv[1]);
++ } else if (strcmp(argv[0], "checkfile") == 0) {
++ if (argc > 3)
++ die("too many arguments");
++ if (strcmp(argv[1], "keytab") == 0)
++ die("checkfile command is not valid for keytabs");
++ status = checkfile(r, options.type, argv[1], argv[2], file);
++ if (status != 0)
++ status = get_file(r, options.type, argv[1], argv[2], file);
+ } else {
+ count = argc + 1;
+ if (strcmp(argv[0], "store") == 0) {
+--- a/client/wallet.pod
++++ b/client/wallet.pod
+@@ -263,7 +263,14 @@ already exist.
+ Check whether an object of type <type> and name <name> already exists. If
+ it does, prints C<yes>; if not, prints C<no>.
+
+-=item checksum
++=item checkfile <type> <name>
++
++Preforms a get command only if the wallet object checksum differs from
++the checksum of the file specified. The -f switch must be specified.
++This command is not valid for keytabs and the wallet object must have
++been stored.
++
++=item checksum <type> <name>
+
+ Returns the checksum for file objects.
+
+--- a/client/internal.h
++++ b/client/internal.h
+@@ -97,6 +97,15 @@ int get_file(struct remctl *, const char
+ const char *name, const char *file);
+
+ /*
++ * Given a remctl object, the command prefix, object type, and object name,
++ * and a file calculate the checksum of the file, get the checksum of
++ * the wallet object, and compare them. Returns 0 if the checksums
++ * match and an exit status on if the don't match.
++ */
++int checkfile(struct remctl *r, const char *prefix, const char *type,
++ const char *name, const char *file);
++
++/*
+ * Given a remctl object, the Kerberos context, the type for the wallet
+ * interface, the name of a keytab object, and a file name, call the correct
+ * wallet commands to download a keytab and write it to that file. If srvtab
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -20,119 +20,119 @@ WALLET_PERL_FLAGS ?=
+ # added to EXTRA_DIST and so that they can be copied over properly for
+ # builddir != srcdir builds.
+ PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
+- perl/lib/Wallet/ACL.pm perl/lib/Wallet/ACL/Base.pm \
+- perl/lib/Wallet/ACL/External.pm perl/lib/Wallet/ACL/Krb5.pm \
+- perl/lib/Wallet/ACL/Krb5/Regex.pm \
+- perl/lib/Wallet/ACL/LDAP/Attribute.pm \
+- perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm \
+- perl/lib/Wallet/ACL/NetDB.pm perl/lib/Wallet/ACL/Nested.pm \
+- perl/lib/Wallet/ACL/NetDB/Root.pm perl/lib/Wallet/Admin.pm \
+- perl/lib/Wallet/Config.pm perl/lib/Wallet/Database.pm \
+- perl/lib/Wallet/Kadmin.pm perl/lib/Wallet/Kadmin/AD.pm \
+- perl/lib/Wallet/Kadmin/Heimdal.pm perl/lib/Wallet/Kadmin/MIT.pm \
+- perl/lib/Wallet/Object/Base.pm perl/lib/Wallet/Object/Duo.pm \
+- perl/lib/Wallet/Object/File.pm perl/lib/Wallet/Object/Keytab.pm \
+- perl/lib/Wallet/Object/Password.pm \
+- perl/lib/Wallet/Object/WAKeyring.pm \
+- perl/lib/Wallet/Policy/Stanford.pm perl/lib/Wallet/Report.pm \
+- perl/lib/Wallet/Schema.pm perl/lib/Wallet/Server.pm \
+- perl/lib/Wallet/Schema/Result/Acl.pm \
+- perl/lib/Wallet/Schema/Result/AclEntry.pm \
+- perl/lib/Wallet/Schema/Result/AclHistory.pm \
+- perl/lib/Wallet/Schema/Result/AclScheme.pm \
+- perl/lib/Wallet/Schema/Result/Duo.pm \
+- perl/lib/Wallet/Schema/Result/Enctype.pm \
+- perl/lib/Wallet/Schema/Result/Flag.pm \
+- perl/lib/Wallet/Schema/Result/KeytabEnctype.pm \
+- perl/lib/Wallet/Schema/Result/KeytabSync.pm \
+- perl/lib/Wallet/Schema/Result/Object.pm \
+- perl/lib/Wallet/Schema/Result/ObjectHistory.pm \
+- perl/lib/Wallet/Schema/Result/SyncTarget.pm \
+- perl/lib/Wallet/Schema/Result/Type.pm \
+- perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql \
+- perl/sql/Wallet-Schema-0.07-0.08-SQLite.sql \
+- perl/sql/Wallet-Schema-0.07-MySQL.sql \
+- perl/sql/Wallet-Schema-0.07-SQLite.sql \
+- perl/sql/Wallet-Schema-0.08-0.09-MySQL.sql \
+- perl/sql/Wallet-Schema-0.08-0.09-PostgreSQL.sql \
+- perl/sql/Wallet-Schema-0.08-0.09-SQLite.sql \
+- perl/sql/Wallet-Schema-0.08-MySQL.sql \
+- perl/sql/Wallet-Schema-0.08-PostgreSQL.sql \
+- perl/sql/Wallet-Schema-0.08-SQLite.sql \
+- perl/sql/Wallet-Schema-0.09-MySQL.sql \
+- perl/sql/Wallet-Schema-0.09-PostgreSQL.sql \
+- perl/sql/Wallet-Schema-0.09-SQLite.sql \
+- perl/sql/Wallet-Schema-0.09-0.10-MySQL.sql \
+- perl/sql/Wallet-Schema-0.09-0.10-PostgreSQL.sql \
+- perl/sql/Wallet-Schema-0.09-0.10-SQLite.sql \
+- perl/sql/Wallet-Schema-0.10-MySQL.sql \
+- perl/sql/Wallet-Schema-0.10-PostgreSQL.sql \
+- perl/sql/Wallet-Schema-0.10-SQLite.sql \
+- perl/sql/wallet-1.3-update-duo.sql perl/t/data/README \
+- perl/t/data/acl-command perl/t/data/duo/integration.json \
+- perl/t/data/duo/integration-ldap.json \
+- perl/t/data/duo/integration-radius.json \
+- perl/t/data/duo/integration-rdp.json perl/t/data/duo/keys.json \
+- perl/t/data/keytab-fake perl/t/data/keytab.conf \
++ perl/lib/Wallet/ACL.pm perl/lib/Wallet/ACL/Base.pm \
++ perl/lib/Wallet/ACL/External.pm perl/lib/Wallet/ACL/Krb5.pm \
++ perl/lib/Wallet/ACL/Krb5/Regex.pm \
++ perl/lib/Wallet/ACL/LDAP/Attribute.pm \
++ perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm \
++ perl/lib/Wallet/ACL/NetDB.pm perl/lib/Wallet/ACL/Nested.pm \
++ perl/lib/Wallet/ACL/NetDB/Root.pm perl/lib/Wallet/Admin.pm \
++ perl/lib/Wallet/Config.pm perl/lib/Wallet/Database.pm \
++ perl/lib/Wallet/Kadmin.pm perl/lib/Wallet/Kadmin/AD.pm \
++ perl/lib/Wallet/Kadmin/Heimdal.pm perl/lib/Wallet/Kadmin/MIT.pm \
++ perl/lib/Wallet/Object/Base.pm perl/lib/Wallet/Object/Duo.pm \
++ perl/lib/Wallet/Object/File.pm perl/lib/Wallet/Object/Keytab.pm \
++ perl/lib/Wallet/Object/Password.pm \
++ perl/lib/Wallet/Object/WAKeyring.pm \
++ perl/lib/Wallet/Policy/Stanford.pm perl/lib/Wallet/Report.pm \
++ perl/lib/Wallet/Schema.pm perl/lib/Wallet/Server.pm \
++ perl/lib/Wallet/Schema/Result/Acl.pm \
++ perl/lib/Wallet/Schema/Result/AclEntry.pm \
++ perl/lib/Wallet/Schema/Result/AclHistory.pm \
++ perl/lib/Wallet/Schema/Result/AclScheme.pm \
++ perl/lib/Wallet/Schema/Result/Duo.pm \
++ perl/lib/Wallet/Schema/Result/Enctype.pm \
++ perl/lib/Wallet/Schema/Result/Flag.pm \
++ perl/lib/Wallet/Schema/Result/KeytabEnctype.pm \
++ perl/lib/Wallet/Schema/Result/KeytabSync.pm \
++ perl/lib/Wallet/Schema/Result/Object.pm \
++ perl/lib/Wallet/Schema/Result/ObjectHistory.pm \
++ perl/lib/Wallet/Schema/Result/SyncTarget.pm \
++ perl/lib/Wallet/Schema/Result/Type.pm \
++ perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql \
++ perl/sql/Wallet-Schema-0.07-0.08-SQLite.sql \
++ perl/sql/Wallet-Schema-0.07-MySQL.sql \
++ perl/sql/Wallet-Schema-0.07-SQLite.sql \
++ perl/sql/Wallet-Schema-0.08-0.09-MySQL.sql \
++ perl/sql/Wallet-Schema-0.08-0.09-PostgreSQL.sql \
++ perl/sql/Wallet-Schema-0.08-0.09-SQLite.sql \
++ perl/sql/Wallet-Schema-0.08-MySQL.sql \
++ perl/sql/Wallet-Schema-0.08-PostgreSQL.sql \
++ perl/sql/Wallet-Schema-0.08-SQLite.sql \
++ perl/sql/Wallet-Schema-0.09-MySQL.sql \
++ perl/sql/Wallet-Schema-0.09-PostgreSQL.sql \
++ perl/sql/Wallet-Schema-0.09-SQLite.sql \
++ perl/sql/Wallet-Schema-0.09-0.10-MySQL.sql \
++ perl/sql/Wallet-Schema-0.09-0.10-PostgreSQL.sql \
++ perl/sql/Wallet-Schema-0.09-0.10-SQLite.sql \
++ perl/sql/Wallet-Schema-0.10-MySQL.sql \
++ perl/sql/Wallet-Schema-0.10-PostgreSQL.sql \
++ perl/sql/Wallet-Schema-0.10-SQLite.sql \
++ perl/sql/wallet-1.3-update-duo.sql perl/t/data/README \
++ perl/t/data/acl-command perl/t/data/duo/integration.json \
++ perl/t/data/duo/integration-ldap.json \
++ perl/t/data/duo/integration-radius.json \
++ perl/t/data/duo/integration-rdp.json perl/t/data/duo/keys.json \
++ perl/t/data/keytab-fake perl/t/data/keytab.conf \
+ perl/t/data/netdb-fake perl/t/data/netdb.conf perl/t/data/perl.conf \
+ perl/t/docs/pod-spelling.t perl/t/docs/pod.t perl/t/general/acl.t \
+- perl/t/general/admin.t perl/t/general/config.t \
+- perl/t/general/init.t perl/t/general/report.t \
+- perl/t/general/server.t perl/t/lib/Util.pm perl/t/object/base.t \
+- perl/t/object/duo.t perl/t/object/duo-ldap.t \
+- perl/t/object/duo-pam.t perl/t/object/duo-radius.t \
++ perl/t/general/admin.t perl/t/general/config.t \
++ perl/t/general/init.t perl/t/general/report.t \
++ perl/t/general/server.t perl/t/lib/Util.pm perl/t/object/base.t \
++ perl/t/object/duo.t perl/t/object/duo-ldap.t \
++ perl/t/object/duo-pam.t perl/t/object/duo-radius.t \
+ perl/t/object/duo-rdp.t perl/t/object/file.t perl/t/object/keytab.t \
+- perl/t/object/password.t perl/t/object/wa-keyring.t \
+- perl/t/policy/stanford.t perl/t/style/minimum-version.t \
++ perl/t/object/password.t perl/t/object/wa-keyring.t \
++ perl/t/policy/stanford.t perl/t/style/minimum-version.t \
+ perl/t/style/strict.t perl/t/util/kadmin.t perl/t/verifier/basic.t \
+- perl/t/verifier/external.t perl/t/verifier/ldap-attr.t \
++ perl/t/verifier/external.t perl/t/verifier/ldap-attr.t \
+ perl/t/verifier/nested.t perl/t/verifier/netdb.t
+
+ # Directories that have to be created in builddir != srcdir builds before
+ # copying PERL_FILES over.
+-PERL_DIRECTORIES = perl perl/lib perl/lib/Wallet perl/lib/Wallet/ACL \
+- perl/lib/Wallet/ACL/Krb5 perl/lib/Wallet/ACL/LDAP \
+- perl/lib/Wallet/ACL/LDAP/Attribute perl/lib/Wallet/ACL/NetDB \
+- perl/lib/Wallet/Kadmin perl/lib/Wallet/Object \
+- perl/lib/Wallet/Policy perl/lib/Wallet/Schema \
+- perl/lib/Wallet/Schema/Result perl/sql perl/t perl/t/data \
++PERL_DIRECTORIES = perl perl/lib perl/lib/Wallet perl/lib/Wallet/ACL \
++ perl/lib/Wallet/ACL/Krb5 perl/lib/Wallet/ACL/LDAP \
++ perl/lib/Wallet/ACL/LDAP/Attribute perl/lib/Wallet/ACL/NetDB \
++ perl/lib/Wallet/Kadmin perl/lib/Wallet/Object \
++ perl/lib/Wallet/Policy perl/lib/Wallet/Schema \
++ perl/lib/Wallet/Schema/Result perl/sql perl/t perl/t/data \
+ perl/t/data/duo perl/t/docs perl/t/general perl/t/lib perl/t/object \
+ perl/t/policy perl/t/style perl/t/util perl/t/verifier
+
+ ACLOCAL_AMFLAGS = -I m4
+-EXTRA_DIST = .gitignore .travis.yml LICENSE README.md bootstrap \
+- client/wallet.pod client/wallet-rekey.pod config/allow-extract \
+- config/keytab config/keytab.acl config/wallet \
+- config/wallet-report.acl docs/design contrib/README \
+- contrib/ad-keytab contrib/ad-keytab.8 \
+- contrib/commerzbank/wallet-history contrib/convert-srvtab-db \
+- contrib/used-principals contrib/wallet-contacts \
+- contrib/wallet-rekey-periodic contrib/wallet-rekey-periodic.8 \
+- contrib/wallet-summary contrib/wallet-summary.8 \
+- contrib/wallet-unknown-hosts contrib/wallet-unknown-hosts.8 \
++EXTRA_DIST = .gitignore .travis.yml LICENSE README.md bootstrap \
++ client/wallet.pod client/wallet-rekey.pod config/allow-extract \
++ config/keytab config/keytab.acl config/wallet \
++ config/wallet-report.acl docs/design contrib/README \
++ contrib/ad-keytab contrib/ad-keytab.8 \
++ contrib/commerzbank/wallet-history contrib/convert-srvtab-db \
++ contrib/used-principals contrib/wallet-contacts \
++ contrib/wallet-rekey-periodic contrib/wallet-rekey-periodic.8 \
++ contrib/wallet-summary contrib/wallet-summary.8 \
++ contrib/wallet-unknown-hosts contrib/wallet-unknown-hosts.8 \
+ docs/design-acl docs/design-api docs/metadata docs/netdb-role-api \
+ docs/notes docs/objects-and-schemes docs/setup docs/stanford-naming \
+- examples/stanford.conf server/keytab-backend.in \
+- server/wallet-admin.in server/wallet-backend.in \
+- server/wallet-report.in tests/README tests/TESTS \
++ examples/stanford.conf server/keytab-backend.in \
++ server/wallet-admin.in server/wallet-backend.in \
++ server/wallet-report.in tests/README tests/TESTS \
+ tests/config/README tests/data/allow-extract tests/data/basic.conf \
+ tests/data/cmd-fake tests/data/cmd-wrapper tests/data/cppcheck.supp \
+ tests/data/fake-data tests/data/fake-kadmin tests/data/fake-keytab \
+- tests/data/fake-keytab-2 tests/data/fake-keytab-foreign \
+- tests/data/fake-keytab-merge tests/data/fake-keytab-old \
+- tests/data/fake-keytab-partial \
++ tests/data/fake-keytab-2 tests/data/fake-keytab-foreign \
++ tests/data/fake-keytab-merge tests/data/fake-keytab-old \
++ tests/data/fake-keytab-partial \
+ tests/data/fake-keytab-partial-result tests/data/fake-keytab-rekey \
+- tests/data/fake-keytab-unknown tests/data/fake-srvtab \
++ tests/data/fake-keytab-unknown tests/data/fake-srvtab \
+ tests/data/full.conf tests/data/perl.conf tests/data/wallet.conf \
+- tests/docs/pod-spelling-t tests/docs/pod-t \
+- tests/docs/spdx-license-t tests/perl/minimum-version-t \
+- tests/perl/module-version-t tests/perl/strict-t \
++ tests/docs/pod-spelling-t tests/docs/pod-t \
++ tests/docs/spdx-license-t tests/perl/minimum-version-t \
++ tests/perl/module-version-t tests/perl/strict-t \
+ tests/server/admin-t tests/server/backend-t tests/server/keytab-t \
+- tests/server/report-t tests/style/obsolete-strings-t \
+- tests/tap/kerberos.sh tests/tap/libtap.sh \
+- tests/tap/perl/Test/RRA.pm tests/tap/perl/Test/RRA/Automake.pm \
+- tests/tap/perl/Test/RRA/Config.pm \
+- tests/tap/perl/Test/RRA/ModuleVersion.pm tests/tap/remctl.sh \
++ tests/server/report-t tests/style/obsolete-strings-t \
++ tests/tap/kerberos.sh tests/tap/libtap.sh \
++ tests/tap/perl/Test/RRA.pm tests/tap/perl/Test/RRA/Automake.pm \
++ tests/tap/perl/Test/RRA/Config.pm \
++ tests/tap/perl/Test/RRA/ModuleVersion.pm tests/tap/remctl.sh \
+ tests/util/xmalloc-t $(PERL_FILES)
+
+ # Supporting convenience libraries used by other targets.
+@@ -142,7 +142,7 @@ portable_libportable_a_SOURCES = portabl
+ portable/uio.h
+ portable_libportable_a_CPPFLAGS = $(KRB5_CPPFLAGS)
+ portable_libportable_a_LIBADD = $(LIBOBJS)
+-util_libutil_a_SOURCES = util/macros.h util/messages-krb5.c \
++util_libutil_a_SOURCES = util/macros.h util/messages-krb5.c \
+ util/messages-krb5.h util/messages.c util/messages.h util/xmalloc.c \
+ util/xmalloc.h
+ util_libutil_a_CPPFLAGS = $(KRB5_CPPFLAGS)
+@@ -150,21 +150,22 @@ util_libutil_a_CPPFLAGS = $(KRB5_CPPFLAG
+ # The private library used by both wallet and wallet-rekey.
+ noinst_LIBRARIES += client/libwallet.a
+ client_libwallet_a_SOURCES = client/file.c client/internal.h client/keytab.c \
+- client/krb5.c client/options.c client/remctl.c client/srvtab.c
++ client/krb5.c client/options.c client/remctl.c client/srvtab.c \
++ client/checkfile.c
+ client_libwallet_a_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS)
+
+ # The client and server programs.
+ bin_PROGRAMS = client/wallet client/wallet-rekey
+ sbin_SCRIPTS = server/keytab-backend server/wallet-admin \
+ server/wallet-backend server/wallet-report
+-client_wallet_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS)
+-client_wallet_LDFLAGS = $(REMCTL_LDFLAGS) $(KRB5_LDFLAGS)
++client_wallet_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS) $(CRYPTO_CPPFLAGS)
++client_wallet_LDFLAGS = $(REMCTL_LDFLAGS) $(KRB5_LDFLAGS) $(CRYPTO_LDFLAGS)
+ client_wallet_LDADD = client/libwallet.a util/libutil.a \
+- portable/libportable.a $(REMCTL_LIBS) $(KRB5_LIBS)
+-client_wallet_rekey_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS)
+-client_wallet_rekey_LDFLAGS = $(REMCTL_LDFLAGS) $(KRB5_LDFLAGS)
++ portable/libportable.a $(REMCTL_LIBS) $(KRB5_LIBS) $(CRYPTO_LIBS)
++client_wallet_rekey_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS) $(CRYPTO_CPPFLAGS)
++client_wallet_rekey_LDFLAGS = $(REMCTL_LDFLAGS) $(KRB5_LDFLAGS) $(CRYPTO_LDFLAGS)
+ client_wallet_rekey_LDADD = client/libwallet.a util/libutil.a \
+- portable/libportable.a $(REMCTL_LIBS) $(KRB5_LIBS)
++ portable/libportable.a $(REMCTL_LIBS) $(KRB5_LIBS) $(CRYPTO_LIBS)
+
+ # The manual pages.
+ dist_man_MANS = client/wallet.1 client/wallet-rekey.1 server/keytab-backend.8 \
+@@ -189,18 +190,18 @@ dist_pkgdata_DATA = perl/sql/Wallet-Sche
+ # compiler warnings enabled as possible.
+ warnings:
+ $(MAKE) V=0 CFLAGS='$(WARNINGS_CFLAGS) $(AM_CFLAGS)' \
+- KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_GCC)'
++ KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_GCC)'
+ $(MAKE) V=0 CFLAGS='$(WARNINGS_CFLAGS) $(AM_CFLAGS)' \
+- KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_GCC)' $(check_PROGRAMS)
++ KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_GCC)' $(check_PROGRAMS)
+
+ # Remove some additional files.
+ CLEANFILES = perl/t/lib/Test/RRA.pm perl/t/lib/Test/RRA/Automake.pm \
+ perl/t/lib/Test/RRA/Config.pm server/keytab-backend \
+ server/wallet-admin server/wallet-backend server/wallet-report
+-MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/compile \
+- build-aux/depcomp build-aux/install-sh build-aux/missing \
+- client/wallet.1 config.h.in config.h.in~ configure \
+- contrib/wallet-report.8 server/keytab-backend.8 \
++MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/compile \
++ build-aux/depcomp build-aux/install-sh build-aux/missing \
++ client/wallet.1 config.h.in config.h.in~ configure \
++ contrib/wallet-report.8 server/keytab-backend.8 \
+ server/wallet-admin.8 server/wallet-backend.8 server/wallet-report.8
+
+ # For each of the Perl scripts, we need to fill in the path to the Perl
+@@ -226,17 +227,17 @@ all-local: perl/blib/lib/Wallet/Config.p
+
+ perl/blib/lib/Wallet/Config.pm: $(srcdir)/perl/lib/Wallet/Config.pm
+ set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \
+- for d in $(PERL_DIRECTORIES) ; do \
++ for d in $(PERL_DIRECTORIES) ; do \
+ [ -d "$(builddir)/$$d" ] || mkdir "$(builddir)/$$d" ; \
+- done ; \
+- for f in $(PERL_FILES) ; do \
++ done ; \
++ for f in $(PERL_FILES) ; do \
+ cp "$(srcdir)/$$f" "$(builddir)/$$f" ; \
+- done ; \
++ done ; \
+ fi
+ $(MKDIR_P) perl/t/lib/Test/RRA
+ $(INSTALL_DATA) $(srcdir)/tests/tap/perl/Test/RRA.pm perl/t/lib/Test/
+ $(INSTALL_DATA) $(srcdir)/tests/tap/perl/Test/RRA/Config.pm \
+- perl/t/lib/Test/RRA/
++ perl/t/lib/Test/RRA/
+ cd perl && $(PERL) Build.PL $(WALLET_PERL_FLAGS)
+ cd perl && ./Build
+
+@@ -251,13 +252,13 @@ perl/blib/lib/Wallet/Config.pm: $(srcdir
+ install-data-local:
+ set -e; flags= ; \
+ case "$(prefix)" in \
+- */_inst) flags="--install_base $(prefix)" ;; \
++ */_inst) flags="--install_base $(prefix)" ;; \
+ esac ; \
+ cd perl && ./Build install $$flags --destdir '$(DESTDIR)'
+
+ clean-local:
+ set -e; if [ -f "perl/Build" ] ; then \
+- cd perl && ./Build realclean ; \
++ cd perl && ./Build realclean ; \
+ fi
+
+ # Remove the Autoconf cache. Remove the files that we copy over if and only
+@@ -265,9 +266,9 @@ clean-local:
+ distclean-local:
+ rm -rf autom4te.cache
+ set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \
+- for f in $(PERL_FILES) ; do \
++ for f in $(PERL_FILES) ; do \
+ rm -f "$(builddir)/$$f" ; \
+- done ; \
++ done ; \
+ fi
+
+ # The bits below are for the test suite, not for the main package.
+@@ -313,8 +314,8 @@ check-local: $(check_PROGRAMS)
+ # Used by maintainers to check the source code with cppcheck.
+ check-cppcheck:
+ cd $(abs_top_srcdir) && cppcheck -q --error-exitcode=2 \
+- --suppressions-list=tests/data/cppcheck.supp \
+- --enable=warning,performance,portability,style .
++ --suppressions-list=tests/data/cppcheck.supp \
++ --enable=warning,performance,portability,style .
+
+ # Alas, we have to disable this check because there's no way to do an
+ # uninstall from Perl.
+--- a/configure.ac
++++ b/configure.ac
+@@ -74,6 +74,9 @@ AC_CHECK_FUNCS([krb5_appdefault_string],
+ AC_LIBOBJ([krb5-extra])
+ RRA_LIB_KRB5_RESTORE
+
++dnl Probe for optional libraries
++RRA_LIB_OPENSSL_OPTIONAL
++
+ dnl Probe for properties of the C library.
+ AC_HEADER_STDBOOL
+ AC_CHECK_HEADERS([strings.h sys/bitypes.h sys/uio.h sys/time.h syslog.h])
+--- /dev/null
++++ b/m4/openssl.m4
+@@ -0,0 +1,115 @@
++dnl Find the compiler and linker flags for OpenSSL.
++dnl
++dnl Finds the compiler and linker flags for linking with both the OpenSSL SSL
++dnl library and its crypto library. Provides the --with-openssl,
++dnl --with-openssl-lib, and --with-openssl-include configure options to
++dnl specify non-standard paths to the OpenSSL libraries.
++dnl
++dnl Provides the macro RRA_LIB_OPENSSL and sets the substitution variables
++dnl OPENSSL_CPPFLAGS, OPENSSL_LDFLAGS, OPENSSL_LIBS, CRYPTO_CPPFLAGS,
++dnl CRYPTO_LDFLAGS, and CRYPTO_LIBS. Also provides RRA_LIB_OPENSSL_SWITCH and
++dnl RRA_LIB_CRYPTO_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the
++dnl SSL or crypto libraries, saving the current values first, and
++dnl RRA_LIB_OPENSSL_RESTORE and RRA_LIB_CRYPTO_RESTORE to restore those
++dnl settings to before the last RRA_LIB_OPENSSL_SWITCH or
++dnl RRA_LIB_CRYPTO_SWITCH. Defines HAVE_OPENSSL and sets rra_use_OPENSSL to
++dnl true if the library is found.
++dnl
++dnl Provides the RRA_LIB_OPENSSL_OPTIONAL macro, which should be used if
++dnl OpenSSL support is optional. This macro will still set the substitution
++dnl variables and shell variables described above, but they'll be empty unless
++dnl OpenSSL libraries are detected. HAVE_OPENSSL will be defined only if the
++dnl library is found.
++dnl
++dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and the lib-helper.m4 framework.
++dnl
++dnl The canonical version of this file is maintained in the rra-c-util
++dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
++dnl
++dnl Written by Russ Allbery <eagle@eyrie.org>
++dnl Copyright 2016, 2018 Russ Allbery <eagle@eyrie.org>
++dnl Copyright 2010, 2013
++dnl The Board of Trustees of the Leland Stanford Junior University
++dnl
++dnl This file is free software; the authors give unlimited permission to copy
++dnl and/or distribute it, with or without modifications, as long as this
++dnl notice is preserved.
++dnl
++dnl SPDX-License-Identifier: FSFULLR
++
++dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to
++dnl versions that include the SSL or crypto flags. Used as a wrapper, with
++dnl RRA_LIB_OPENSSL_RESTORE or RRA_LIB_CRYPTO_RESTORE, around tests.
++AC_DEFUN([RRA_LIB_OPENSSL_SWITCH], [RRA_LIB_HELPER_SWITCH([OPENSSL])])
++AC_DEFUN([RRA_LIB_CRYPTO_SWITCH], [RRA_LIB_HELPER_SWITCH([CRYPTO])])
++
++dnl Restore CPPFLAGS, LDFLAGS, and LIBS to their previous values (before
++dnl RRA_LIB_OPENSSL_SWITCH or RRA_LIB_CRYPTO_SWITCH were called).
++AC_DEFUN([RRA_LIB_OPENSSL_RESTORE], [RRA_LIB_HELPER_RESTORE([OPENSSL])])
++AC_DEFUN([RRA_LIB_CRYPTO_RESTORE], [RRA_LIB_HELPER_RESTORE([CRYPTO])])
++
++dnl Check for the OpenSSL and crypto libraries and assemble OPENSSL_LIBS and
++dnl CRYPTO_LIBS. Helper function for _RRA_LIB_OPENSSL_INTERNAL. Must be
++dnl called with RRA_LIB_OPENSSL_SWITCH enabled.
++AC_DEFUN([_RRA_LIB_OPENSSL_INTERNAL_LIBS],
++[rra_openssl_extra=
++ LIBS=
++ AS_IF([test x"$rra_reduced_depends" != xtrue],
++ [AC_SEARCH_LIBS([dlopen], [dl])])
++ rra_openssl_extra="$LIBS"
++ LIBS="$rra_OPENSSL_save_LIBS"
++ AC_CHECK_LIB([crypto], [AES_cbc_encrypt],
++ [CRYPTO_LIBS="-lcrypto $rra_openssl_extra"],
++ [AS_IF([test x"$1" = xtrue],
++ [AC_MSG_ERROR([cannot find usable OpenSSL crypto library])])],
++ [$rra_openssl_extra])
++ AS_IF([test x"$rra_reduced_depends" = xtrue],
++ [AC_CHECK_LIB([ssl], [SSL_accept], [OPENSSL_LIBS=-lssl],
++ [AS_IF([test x"$1" = xtrue],
++ [AC_MSG_ERROR([cannot find usable OpenSSL library])])])],
++ [AC_CHECK_LIB([ssl], [SSL_accept],
++ [OPENSSL_LIBS="-lssl $CRYPTO_LIBS"],
++ [AS_IF([test x"$1" = xtrue],
++ [AC_MSG_ERROR([cannot find usable OpenSSL library])])],
++ [$CRYPTO_LIBS])])])
++
++dnl Checks if the OpenSSL header and OpenSSL and crypto libraries are present.
++dnl The single argument, if "true", says to fail if the OpenSSL SSL library
++dnl could not be found.
++AC_DEFUN([_RRA_LIB_OPENSSL_INTERNAL],
++[AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS])
++ RRA_LIB_HELPER_PATHS([OPENSSL])
++ CRYPTO_CPPFLAGS="$OPENSSL_CPPFLAGS"
++ CRYPTO_LDFLAGS="$OPENSSL_LDFLAGS"
++ CRYPTO_LIBS=
++ AC_SUBST([CRYPTO_CPPFLAGS])
++ AC_SUBST([CRYPTO_LDFLAGS])
++ AC_SUBST([CRYPTO_LIBS])
++ RRA_LIB_OPENSSL_SWITCH
++ AC_CHECK_HEADER([openssl/ssl.h],
++ [_RRA_LIB_OPENSSL_INTERNAL_LIBS([$1])],
++ [AS_IF([test x"$1" = xtrue],
++ [AC_MSG_ERROR([cannot find usable OpenSSL header])])])
++ RRA_LIB_OPENSSL_RESTORE])
++
++dnl The main macro for packages with mandatory OpenSSL support.
++AC_DEFUN([RRA_LIB_OPENSSL],
++[RRA_LIB_HELPER_VAR_INIT([OPENSSL])
++ RRA_LIB_HELPER_WITH([openssl], [OpenSSL], [OPENSSL])
++ _RRA_LIB_OPENSSL_INTERNAL([true])
++ rra_use_OPENSSL=true
++ AC_DEFINE([HAVE_OPENSSL], 1, [Define if libssl is available.])])
++
++dnl The main macro for packages with optional OpenSSL support.
++AC_DEFUN([RRA_LIB_OPENSSL_OPTIONAL],
++[RRA_LIB_HELPER_VAR_INIT([OPENSSL])
++ RRA_LIB_HELPER_WITH_OPTIONAL([openssl], [OpenSSL], [OPENSSL])
++ AS_IF([test x"$rra_use_OPENSSL" != xfalse],
++ [AS_IF([test x"$rra_use_OPENSSL" = xtrue],
++ [_RRA_LIB_OPENSSL_INTERNAL([true])],
++ [_RRA_LIB_OPENSSL_INTERNAL([false])])])
++ AS_IF([test x"$OPENSSL_LIBS" = x],
++ [RRA_LIB_HELPER_VAR_CLEAR([OPENSSL])
++ RRA_LIB_HELPER_VAR_CLEAR([CRYPTO])],
++ [rra_use_OPENSSL=true
++ AC_DEFINE([HAVE_OPENSSL], 1, [Define if libssl is available.])])])
+--- /dev/null
++++ b/m4/lib-helper.m4
+@@ -0,0 +1,149 @@
++dnl Helper functions to manage compiler variables.
++dnl
++dnl These are a wide variety of helper macros to make it easier to construct
++dnl standard macros to probe for a library and to set library-specific
++dnl CPPFLAGS, LDFLAGS, and LIBS shell substitution variables. Most of them
++dnl take as one of the arguments the prefix string to use for variables, which
++dnl is usually something like "KRB5" or "GSSAPI".
++dnl
++dnl Depends on RRA_SET_LDFLAGS.
++dnl
++dnl The canonical version of this file is maintained in the rra-c-util
++dnl package, available at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
++dnl
++dnl Written by Russ Allbery <eagle@eyrie.org>
++dnl Copyright 2018 Russ Allbery <eagle@eyrie.org>
++dnl Copyright 2011, 2013
++dnl The Board of Trustees of the Leland Stanford Junior University
++dnl
++dnl This file is free software; the authors give unlimited permission to copy
++dnl and/or distribute it, with or without modifications, as long as this
++dnl notice is preserved.
++dnl
++dnl SPDX-License-Identifier: FSFULLR
++
++dnl Add the library flags to the default compiler flags and then remove them.
++dnl
++dnl To use these macros, pass the prefix string used for the variables as the
++dnl only argument. For example, to use these for a library with KRB5 as a
++dnl prefix, one would use:
++dnl
++dnl AC_DEFUN([RRA_LIB_KRB5_SWITCH], [RRA_LIB_HELPER_SWITCH([KRB5])])
++dnl AC_DEFUN([RRA_LIB_KRB5_RESTORE], [RRA_LIB_HELPER_RESTORE([KRB5])])
++dnl
++dnl Then, wrap checks for library features with RRA_LIB_KRB5_SWITCH and
++dnl RRA_LIB_KRB5_RESTORE.
++AC_DEFUN([RRA_LIB_HELPER_SWITCH],
++[rra_$1[]_save_CPPFLAGS="$CPPFLAGS"
++ rra_$1[]_save_LDFLAGS="$LDFLAGS"
++ rra_$1[]_save_LIBS="$LIBS"
++ CPPFLAGS="$$1[]_CPPFLAGS $CPPFLAGS"
++ LDFLAGS="$$1[]_LDFLAGS $LDFLAGS"
++ LIBS="$$1[]_LIBS $LIBS"])
++
++AC_DEFUN([RRA_LIB_HELPER_RESTORE],
++[CPPFLAGS="$rra_$1[]_save_CPPFLAGS"
++ LDFLAGS="$rra_$1[]_save_LDFLAGS"
++ LIBS="$rra_$1[]_save_LIBS"])
++
++dnl Given _root, _libdir, and _includedir variables set for a library (set by
++dnl RRA_LIB_HELPER_WITH*), set the LDFLAGS and CPPFLAGS variables for that
++dnl library accordingly. Takes the variable prefix as the only argument.
++AC_DEFUN([RRA_LIB_HELPER_PATHS],
++[AS_IF([test x"$rra_$1[]_libdir" != x],
++ [$1[]_LDFLAGS="-L$rra_$1[]_libdir"],
++ [AS_IF([test x"$rra_$1[]_root" != x],
++ [RRA_SET_LDFLAGS([$1][_LDFLAGS], [${rra_$1[]_root}])])])
++ AS_IF([test x"$rra_$1[]_includedir" != x],
++ [$1[]_CPPFLAGS="-I$rra_$1[]_includedir"],
++ [AS_IF([test x"$rra_$1[]_root" != x],
++ [AS_IF([test x"$rra_$1[]_root" != x/usr],
++ [$1[]_CPPFLAGS="-I${rra_$1[]_root}/include"])])])])
++
++dnl Check whether a library works. This is used as a sanity check on the
++dnl results of *-config shell scripts. Takes four arguments; the first, if
++dnl "true", says that a working library is mandatory and errors out if it
++dnl doesn't. The second is the variable prefix. The third is a function to
++dnl look for that should be in the libraries. The fourth is the
++dnl human-readable name of the library for error messages.
++AC_DEFUN([RRA_LIB_HELPER_CHECK],
++[RRA_LIB_HELPER_SWITCH([$2])
++ AC_CHECK_FUNC([$3], [],
++ [AS_IF([test x"$1" = xtrue],
++ [AC_MSG_FAILURE([unable to link with $4 library])])
++ $2[]_CPPFLAGS=
++ $2[]_LDFLAGS=
++ $2[]_LIBS=])
++ RRA_LIB_HELPER_RESTORE([$2])])
++
++dnl Initialize the variables used by a library probe and set the appropriate
++dnl ones as substitution variables. Takes the library variable prefix as its
++dnl only argument.
++AC_DEFUN([RRA_LIB_HELPER_VAR_INIT],
++[rra_$1[]_root=
++ rra_$1[]_libdir=
++ rra_$1[]_includedir=
++ rra_use_$1=
++ $1[]_CPPFLAGS=
++ $1[]_LDFLAGS=
++ $1[]_LIBS=
++ AC_SUBST([$1][_CPPFLAGS])
++ AC_SUBST([$1][_LDFLAGS])
++ AC_SUBST([$1][_LIBS])])
++
++dnl Unset all of the variables used by a library probe. Used with the
++dnl _OPTIONAL versions of header probes when a header or library wasn't found
++dnl and therefore the library isn't usable.
++AC_DEFUN([RRA_LIB_HELPER_VAR_CLEAR],
++[$1[]_CPPFLAGS=
++ $1[]_LDFLAGS=
++ $1[]_LIBS=])
++
++dnl Handles --with options for a non-optional library. First argument is the
++dnl base for the switch names. Second argument is the short description.
++dnl Third argument is the variable prefix. The variables set are used by
++dnl RRA_LIB_HELPER_PATHS.
++AC_DEFUN([RRA_LIB_HELPER_WITH],
++[AC_ARG_WITH([$1],
++ [AS_HELP_STRING([--with-][$1][=DIR],
++ [Location of $2 headers and libraries])],
++ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
++ [rra_$3[]_root="$withval"])])
++ AC_ARG_WITH([$1][-include],
++ [AS_HELP_STRING([--with-][$1][-include=DIR],
++ [Location of $2 headers])],
++ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
++ [rra_$3[]_includedir="$withval"])])
++ AC_ARG_WITH([$1][-lib],
++ [AS_HELP_STRING([--with-][$1][-lib=DIR],
++ [Location of $2 libraries])],
++ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
++ [rra_$3[]_libdir="$withval"])])])
++
++dnl Handles --with options for an optional library, so --with-<library> can
++dnl cause the checks to be skipped entirely or become mandatory. Sets an
++dnl rra_use_PREFIX variable to true or false if the library is explicitly
++dnl enabled or disabled.
++dnl
++dnl First argument is the base for the switch names. Second argument is the
++dnl short description. Third argument is the variable prefix.
++dnl
++dnl The variables set are used by RRA_LIB_HELPER_PATHS.
++AC_DEFUN([RRA_LIB_HELPER_WITH_OPTIONAL],
++[AC_ARG_WITH([$1],
++ [AS_HELP_STRING([--with-][$1][@<:@=DIR@:>@],
++ [Location of $2 headers and libraries])],
++ [AS_IF([test x"$withval" = xno],
++ [rra_use_$3=false],
++ [AS_IF([test x"$withval" != xyes], [rra_$3[]_root="$withval"])
++ rra_use_$3=true])])
++ AC_ARG_WITH([$1][-include],
++ [AS_HELP_STRING([--with-][$1][-include=DIR],
++ [Location of $2 headers])],
++ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
++ [rra_$3[]_includedir="$withval"])])
++ AC_ARG_WITH([$1][-lib],
++ [AS_HELP_STRING([--with-][$1][-lib=DIR],
++ [Location of $2 libraries])],
++ [AS_IF([test x"$withval" != xyes && test x"$withval" != xno],
++ [rra_$3[]_libdir="$withval"])])])
diff --git a/debian/patches/0017-checkfile.patch b/debian/patches/0017-checkfile.patch
new file mode 100644
index 0000000..166c7b9
--- /dev/null
+++ b/debian/patches/0017-checkfile.patch
@@ -0,0 +1,77 @@
+Update client man page, suppress spurious check file warning
+
+* The POD was updated with the original checkfile changes, but a
+ new man page was not generated. This change updates the man page
+ for the client.
+
+* When issuing a checkfile command against an password object that
+ exists but has not been stored yet a warning message was being
+ generated. This warning is confusing since the command succeeds
+ since the password is generated and downloaded.
+--- a/client/wallet.1
++++ b/client/wallet.1
+@@ -129,7 +129,7 @@
+ .\" ========================================================================
+ .\"
+ .IX Title "WALLET 1"
+-.TH WALLET 1 "2018-06-04" "1.4" "wallet"
++.TH WALLET 1 "2020-12-14" "perl v5.26.1" "User Contributed Perl Documentation"
+ .\" For nroff, turn off justification. Always turn off hyphenation; it makes
+ .\" way too many mistakes in technical documents.
+ .if n .ad l
+@@ -291,7 +291,15 @@ For more information on attributes, see
+ .IP "acl add <id> <scheme> <identifier>" 4
+ .IX Item "acl add <id> <scheme> <identifier>"
+ Add an entry with <scheme> and <identifier> to the \s-1ACL\s0 <id>. <id> may be
+-either the name of an \s-1ACL\s0 or its numeric identifier.
++either the name of an \s-1ACL\s0 or its numeric identifier. Three schemes are
++supported: krb5, netdb, and ldap-attr. The netdb and ldap-attr must
++be configured before they can be used. Examples:
++.Sp
++.Vb 3
++\& wallet acl add config/db krb5 mac@CA\-ZEPHYR.ORG
++\& wallet acl add config/db netdb host/keddie.ca\-zephyr.org
++\& wallet acl add config/db ldap\-attr czPrivilegeGroup=admin
++.Ve
+ .IP "acl check <id>" 4
+ .IX Item "acl check <id>"
+ Check whether an \s-1ACL\s0 with the \s-1ID\s0 <id> already exists. If it does, prints
+@@ -356,6 +364,15 @@ already exist.
+ .IX Item "check <type> <name>"
+ Check whether an object of type <type> and name <name> already exists. If
+ it does, prints \f(CW\*(C`yes\*(C'\fR; if not, prints \f(CW\*(C`no\*(C'\fR.
++.IP "checkfile <type> <name>" 4
++.IX Item "checkfile <type> <name>"
++Preforms a get command only if the wallet object checksum differs from
++the checksum of the file specified. The \-f switch must be specified.
++This command is not valid for keytabs and the wallet object must have
++been stored.
++.IP "checksum <type> <name>" 4
++.IX Item "checksum <type> <name>"
++Returns the checksum for file objects.
+ .IP "comment <type> <name> [<comment>]" 4
+ .IX Item "comment <type> <name> [<comment>]"
+ If <comment> is not given, displays the current comment for the object
+@@ -474,7 +491,7 @@ will attempt to automatically create it
+ .IX 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 \fB\-f\fR option was
+-given. This will generate new data in the object, and only works for
++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.
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -344,7 +344,10 @@ sub checksum {
+ my $path = $self->file_path;
+ my $this_checksum;
+ my $this_data;
+- my $this_endata = read_file($path);
++ my $this_endata;
++ if (-e $path) {
++ $this_endata = read_file($path);
++ }
+ if ($Wallet::Config::ENCRYPTION_PREFIX) {
+ $this_data = $self->file_decrypt($this_endata, $user, $host, $time)
+ } else {
diff --git a/debian/patches/0018-ad-length.patch b/debian/patches/0018-ad-length.patch
new file mode 100644
index 0000000..fe5c0d0
--- /dev/null
+++ b/debian/patches/0018-ad-length.patch
@@ -0,0 +1,36 @@
+--- a/contrib/ad-keytab
++++ b/contrib/ad-keytab
+@@ -3,7 +3,7 @@
+ # Create, update, delete, and display keytabs stored in Active Directory.
+ #
+ # Written by Bill MacAllister <whm@dropbox.com>
+-# Copyright 2016 Dropbox, Inc.
++# Copyright 2016-21 Dropbox, Inc.
+ #
+ # SPDX-License-Identifier: MIT
+
+@@ -227,7 +227,7 @@ sub kerberos_attrs {
+ $attr{cn} =~ s/.*?=//xms;
+ }
+ } else {
+- if (length($attr{cn})>20) {
++ if (length($attr{cn})>19) {
+ my $cnt = 0;
+ my $this_dn;
+ my $this_prefix = substr($attr{cn}, 0, 18);
+--- a/perl/lib/Wallet/Config.pm
++++ b/perl/lib/Wallet/Config.pm
+@@ -508,11 +508,11 @@ The maximum length of a unique identifie
+ Directory keytab objects. If the identifier exceeds this length then it will
+ be truncated and an integer will be appended to the end of the identifier.
+ This parameter is here in hopes that at some point in the future Microsoft
+-will remove the limitation.
++will remove the limitation. The default value is 19.
+
+ =cut
+
+-our $AD_SERVICE_LENGTH = '20';
++our $AD_SERVICE_LENGTH = '19';
+
+ =item AD_SERVICE_LIMIT
+
diff --git a/debian/patches/0019-password-encrypt.patch b/debian/patches/0019-password-encrypt.patch
new file mode 100644
index 0000000..36d2366
--- /dev/null
+++ b/debian/patches/0019-password-encrypt.patch
@@ -0,0 +1,122 @@
+--- a/perl/lib/Wallet/Object/File.pm
++++ b/perl/lib/Wallet/Object/File.pm
+@@ -249,8 +249,9 @@ sub _file_crypt {
+ return $return_string;
+ }
+
+-sub file_decrypt {
+- my ($self, $data, $user, $host, $time) = @_;
++# Given a string decrypt it.
++sub string_decrypt {
++ my ($self, $data) = @_;
+ my $key = $self->_get_crypt_key();
+ my $undata;
+ if (defined (&Wallet::Config::file_crypt)) {
+@@ -264,6 +265,15 @@ sub file_decrypt {
+ }
+ $undata = $self->_file_crypt('decrypt', $key, $data);
+ }
++ return $undata;
++}
++
++# Given a string decrypt it. If the string is not encrypted then the
++# input string string will match the decrypted string and the string
++# will be encrypted and stored to disk.
++sub file_decrypt {
++ my ($self, $data, $user, $host, $time) = @_;
++ my $undata = $self->string_decrypt($data);
+ if ($undata eq $data) {
+ $self->store($data, $user, $host, $time);
+ }
+--- a/perl/lib/Wallet/Object/Password.pm
++++ b/perl/lib/Wallet/Object/Password.pm
+@@ -84,6 +84,44 @@ sub _pwd_xkcd {
+ return $pass;
+ }
+
++# Read the password file to disk.
++sub _read_pw_file {
++ my ($self, $path) = @_;
++ my $id = $self->{type} . ':' . $self->{name};
++
++ unless (open (FILE, '<', $path)) {
++ $self->error ("cannot get $id: object has not been stored");
++ return;
++ }
++ local $/;
++ my $data = <FILE>;
++ unless (close FILE) {
++ $self->error ("cannot get $id: $!");
++ return;
++ }
++ return $data;
++}
++
++# Write the password file to disk.
++sub _write_pw_file {
++ my ($self, $path, $data) = @_;
++ my $id = $self->{type} . ':' . $self->{name};
++
++ unless (open (FILE, '>', $path)) {
++ $self->error ("cannot open $path $!\n");
++ return 1;
++ }
++ if ($Wallet::Config::LDAP_SECRET) {
++ $data = Wallet::Object::File->file_encrypt($data);
++ }
++ print FILE $data;
++ unless (close FILE) {
++ $self->error ("cannot store $id: $!");
++ return 1;
++ }
++ return 0;
++}
++
+ ##############################################################################
+ # Shared methods
+ ##############################################################################
+@@ -135,34 +173,25 @@ sub retrieve {
+ }
+ return;
+ }
+- unless (open (FILE, '>', $path)) {
+- $self->error ("cannot open $path $!\n");
++ if ($self->_write_pw_file($path, $pass)) {
+ return;
+ }
+- if ($Wallet::Config::LDAP_SECRET) {
+- $pass = Wallet::Object::File->file_encrypt($pass);
+- }
+- print FILE $pass;
+ $self->log_action ('store', $user, $host, $time);
+- unless (close FILE) {
+- $self->error ("cannot get $id: $!");
+- return;
+- }
+ }
+
+- unless (open (FILE, '<', $path)) {
+- $self->error ("cannot get $id: object has not been stored");
+- return;
+- }
+- local $/;
+- my $data = <FILE>;
+- unless (close FILE) {
+- $self->error ("cannot get $id: $!");
++ my $data = $self->_read_pw_file($path);
++ if (!$data) {
+ return;
+ }
+ $self->log_action ($operation, $user, $host, $time);
+ if ($Wallet::Config::LDAP_SECRET) {
+- $data = Wallet::Object::File->file_decrypt($data);
++ my $undata = Wallet::Object::File->string_decrypt($data);
++ if ($undata eq $data) {
++ my $endata = Wallet::Object::File->file_encrypt($data);
++ $self->_write_pw_file($path, $endata);
++ $self->log_action ($operation, $user, $host, $time);
++ }
++ $data = $undata;
+ }
+ return $data;
+ }
diff --git a/debian/patches/0020-retire-webauth.patch b/debian/patches/0020-retire-webauth.patch
new file mode 100644
index 0000000..0ccf159
--- /dev/null
+++ b/debian/patches/0020-retire-webauth.patch
@@ -0,0 +1,1359 @@
+Index: wallet/perl/lib/Wallet/Object/WAKeyring.pm
+===================================================================
+--- wallet.orig/perl/lib/Wallet/Object/WAKeyring.pm 2022-06-18 01:53:50.800081794 +0000
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,367 +0,0 @@
+-# Wallet::Object::WAKeyring -- WebAuth keyring object implementation
+-#
+-# Written by Russ Allbery <eagle@eyrie.org>
+-# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+-# Copyright 2012-2014
+-# The Board of Trustees of the Leland Stanford Junior University
+-#
+-# SPDX-License-Identifier: MIT
+-
+-##############################################################################
+-# Modules and declarations
+-##############################################################################
+-
+-package Wallet::Object::WAKeyring;
+-
+-use 5.008;
+-use strict;
+-use warnings;
+-
+-use Digest::MD5 qw(md5_hex);
+-use Fcntl qw(LOCK_EX);
+-use Wallet::Config;
+-use Wallet::Object::Base;
+-use WebAuth 3.06 qw(WA_KEY_AES WA_AES_128);
+-
+-our @ISA = qw(Wallet::Object::Base);
+-our $VERSION = '1.04';
+-
+-##############################################################################
+-# File naming
+-##############################################################################
+-
+-# Returns the path into which that keyring object will be stored or undef on
+-# error. On error, sets the internal error.
+-sub file_path {
+- my ($self) = @_;
+- my $name = $self->{name};
+- unless ($Wallet::Config::WAKEYRING_BUCKET) {
+- $self->error ('WebAuth keyring support not configured');
+- return;
+- }
+- unless ($name) {
+- $self->error ('WebAuth keyring objects may not have empty names');
+- return;
+- }
+- my $hash = substr (md5_hex ($name), 0, 2);
+- $name =~ s/([^\w-])/sprintf ('%%%02X', ord ($1))/ge;
+- my $parent = "$Wallet::Config::WAKEYRING_BUCKET/$hash";
+- unless (-d $parent || mkdir ($parent, 0700)) {
+- $self->error ("cannot create keyring bucket $hash: $!");
+- return;
+- }
+- return "$Wallet::Config::WAKEYRING_BUCKET/$hash/$name";
+-}
+-
+-##############################################################################
+-# Core methods
+-##############################################################################
+-
+-# Override destroy to delete the file as well.
+-sub destroy {
+- my ($self, $user, $host, $time) = @_;
+- my $id = $self->{type} . ':' . $self->{name};
+- my $path = $self->file_path;
+- if (defined ($path) && -f $path && !unlink ($path)) {
+- $self->error ("cannot delete $id: $!");
+- return;
+- }
+- return $self->SUPER::destroy ($user, $host, $time);
+-}
+-
+-# Update the keyring if needed, and then return the contents of the current
+-# keyring.
+-sub get {
+- my ($self, $user, $host, $time) = @_;
+- $time ||= time;
+- my $id = $self->{type} . ':' . $self->{name};
+- if ($self->flag_check ('locked')) {
+- $self->error ("cannot get $id: object is locked");
+- return;
+- }
+- my $path = $self->file_path;
+- return unless defined $path;
+-
+- # Create a WebAuth context and ensure we can load the relevant modules.
+- my $wa = eval { WebAuth->new };
+- if ($@) {
+- $self->error ("cannot initialize WebAuth: $@");
+- return;
+- }
+-
+- # Check if the keyring already exists. If not, create a new one with a
+- # single key that's immediately valid and two more that will become valid
+- # in the future.
+- #
+- # If the keyring does already exist, get a lock on the file. At the end
+- # of this process, we'll do an atomic update and then drop our lock.
+- #
+- # FIXME: There are probably better ways to do this. There are some race
+- # conditions here, particularly with new keyrings.
+- unless (open (FILE, '+<', $path)) {
+- my $data;
+- eval {
+- my $key = $wa->key_create (WA_KEY_AES, WA_AES_128);
+- my $ring = $wa->keyring_new ($key);
+- $key = $wa->key_create (WA_KEY_AES, WA_AES_128);
+- my $valid = time + $Wallet::Config::WAKEYRING_REKEY_INTERVAL;
+- $ring->add (time, $valid, $key);
+- $key = $wa->key_create (WA_KEY_AES, WA_AES_128);
+- $valid += $Wallet::Config::WAKEYRING_REKEY_INTERVAL;
+- $ring->add (time, $valid, $key);
+- $data = $ring->encode;
+- $ring->write ($path);
+- };
+- if ($@) {
+- $self->error ("cannot create new keyring");
+- return;
+- };
+- $self->log_action ('get', $user, $host, $time);
+- return $data;
+- }
+- unless (flock (FILE, LOCK_EX)) {
+- $self->error ("cannot get lock on keyring: $!");
+- return;
+- }
+-
+- # Read the keyring.
+- my $ring = eval { WebAuth::Keyring->read ($wa, $path) };
+- if ($@) {
+- $self->error ("cannot read keyring: $@");
+- return;
+- }
+-
+- # If the most recent key has a valid-after older than now +
+- # WAKEYRING_REKEY_INTERVAL, we generate a new key with a valid_after of
+- # now + 2 * WAKEYRING_REKEY_INTERVAL.
+- my ($count, $newest) = (0, 0);
+- for my $entry ($ring->entries) {
+- $count++;
+- if ($entry->valid_after > $newest) {
+- $newest = $entry->valid_after;
+- }
+- }
+- eval {
+- if ($newest <= time + $Wallet::Config::WAKEYRING_REKEY_INTERVAL) {
+- my $valid = time + 2 * $Wallet::Config::WAKEYRING_REKEY_INTERVAL;
+- my $key = $wa->key_create (WA_KEY_AES, WA_AES_128);
+- $ring->add (time, $valid, $key);
+- }
+- };
+- if ($@) {
+- $self->error ("cannot add new key: $@");
+- return;
+- }
+-
+- # If there are any keys older than the purge interval, remove them, but
+- # only do so if we have more than three keys (the one that's currently
+- # active, the one that's going to come active in the rekey interval, and
+- # the one that's going to come active after that.
+- #
+- # FIXME: Be sure that we don't remove the last currently-valid key.
+- my $cutoff = time - $Wallet::Config::WAKEYRING_PURGE_INTERVAL;
+- my $i = 0;
+- my @purge;
+- if ($count > 3) {
+- for my $entry ($ring->entries) {
+- if ($entry->creation < $cutoff) {
+- push (@purge, $i);
+- }
+- $i++;
+- }
+- }
+- if (@purge && $count - @purge >= 3) {
+- eval {
+- for my $key (reverse @purge) {
+- $ring->remove ($key);
+- }
+- };
+- if ($@) {
+- $self->error ("cannot remove old keys: $@");
+- return;
+- }
+- }
+-
+- # Encode the key.
+- my $data = eval { $ring->encode };
+- if ($@) {
+- $self->error ("cannot encode keyring: $@");
+- return;
+- }
+-
+- # Write the new keyring to the path.
+- eval { $ring->write ($path) };
+- if ($@) {
+- $self->error ("cannot store new keyring: $@");
+- return;
+- }
+- close FILE;
+- $self->log_action ('get', $user, $host, $time);
+- return $data;
+-}
+-
+-# Store the file on the wallet server.
+-#
+-# FIXME: Check the provided keyring for validity.
+-sub store {
+- my ($self, $data, $user, $host, $time) = @_;
+- $time ||= time;
+- my $id = $self->{type} . ':' . $self->{name};
+- if ($self->flag_check ('locked')) {
+- $self->error ("cannot store $id: object is locked");
+- return;
+- }
+- if ($Wallet::Config::FILE_MAX_SIZE) {
+- my $max = $Wallet::Config::FILE_MAX_SIZE;
+- if (length ($data) > $max) {
+- $self->error ("data exceeds maximum of $max bytes");
+- return;
+- }
+- }
+- my $path = $self->file_path;
+- return unless $path;
+- unless (open (FILE, '>', $path)) {
+- $self->error ("cannot store $id: $!");
+- return;
+- }
+- unless (print FILE ($data) and close FILE) {
+- $self->error ("cannot store $id: $!");
+- close FILE;
+- return;
+- }
+- $self->log_action ('store', $user, $host, $time);
+- return 1;
+-}
+-
+-1;
+-__END__
+-
+-##############################################################################
+-# Documentation
+-##############################################################################
+-
+-=for stopwords
+-WebAuth keyring keyrings API HOSTNAME DATETIME keytab AES rekey Allbery
+-
+-=head1 NAME
+-
+-Wallet::Object::WAKeyring - WebAuth keyring object implementation for wallet
+-
+-=head1 SYNOPSIS
+-
+- my ($user, $host, $time);
+- my @name = qw(wa-keyring www.stanford.edu);
+- my @trace = ($user, $host, $time);
+- my $object = Wallet::Object::WAKeyring->create (@name, $schema, $trace);
+- my $keyring = $object->get (@trace);
+- unless ($object->store ($keyring)) {
+- die $object->error, "\n";
+- }
+- $object->destroy (@trace);
+-
+-=head1 DESCRIPTION
+-
+-Wallet::Object::WAKeyring is a representation of a WebAuth keyring in the
+-wallet. It implements the wallet object API and provides the necessary
+-glue to store a keyring on the wallet server, retrieve it, update the
+-keyring with new keys automatically as needed, purge old keys
+-automatically, and delete the keyring when the object is deleted.
+-
+-WebAuth keyrings hold one or more keys. Each key has a creation time and
+-a validity time. The key cannot be used until its validity time has been
+-reached. This permits safe key rotation: a new key is added with a
+-validity time in the future, and then the keyring is updated everywhere it
+-needs to be before that validity time is reached. This wallet object
+-automatically handles key rotation by adding keys with validity dates in
+-the future and removing keys with creation dates substantially in the
+-past.
+-
+-To use this object, various configuration options specifying where to
+-store the keyrings and how to handle key rotation must be set. See
+-Wallet::Config for details on these configuration parameters and
+-information about how to set wallet configuration.
+-
+-=head1 METHODS
+-
+-This object mostly inherits from Wallet::Object::Base. See the
+-documentation for that class for all generic methods. Below are only
+-those methods that are overridden or behave specially for this
+-implementation.
+-
+-=over 4
+-
+-=item destroy(PRINCIPAL, HOSTNAME [, DATETIME])
+-
+-Destroys a WebAuth keyring object by removing it from the database and
+-deleting the corresponding file on the wallet server. Returns true on
+-success and false on failure. The caller should call error() to get the
+-error message after a failure. PRINCIPAL, HOSTNAME, and DATETIME are
+-stored as history information. PRINCIPAL should be the user who is
+-destroying the object. If DATETIME isn't given, the current time is used.
+-
+-=item get(PRINCIPAL, HOSTNAME [, DATETIME])
+-
+-Either creates a new WebAuth keyring (if this object has not bee stored or
+-retrieved before) or does any necessary periodic maintenance on the
+-keyring and then returns its data. The caller should call error() to get
+-the error message if get() returns undef. PRINCIPAL, HOSTNAME, and
+-DATETIME are stored as history information. PRINCIPAL should be the user
+-who is downloading the keytab. If DATETIME isn't given, the current time
+-is used.
+-
+-If this object has never been stored or retrieved before, a new keyring
+-will be created with three 128-bit AES keys: one that is immediately
+-valid, one that will become valid after the rekey interval, and one that
+-will become valid after twice the rekey interval.
+-
+-If keyring data for this object already exists, the creation and validity
+-dates for each key in the keyring will be examined. If the key with the
+-validity date the farthest into the future has a date that's less than or
+-equal to the current time plus the rekey interval, a new 128-bit AES key
+-will be added to the keyring with a validity time of twice the rekey
+-interval in the future. Finally, all keys with a creation date older than
+-the configured purge interval will be removed provided that the keyring
+-has at least three keys
+-
+-=item store(DATA, PRINCIPAL, HOSTNAME [, DATETIME])
+-
+-Store DATA as the current contents of the WebAuth keyring object. Note
+-that this is not checked for validity, just assumed to be a valid keyring.
+-Any existing data will be overwritten. Returns true on success and false
+-on failure. The caller should call error() to get the error message after
+-a failure. PRINCIPAL, HOSTNAME, and DATETIME are stored as history
+-information. PRINCIPAL should be the user who is destroying the object.
+-If DATETIME isn't given, the current time is used.
+-
+-If FILE_MAX_SIZE is set in the wallet configuration, a store() of DATA
+-larger than that configuration setting will be rejected.
+-
+-=back
+-
+-=head1 FILES
+-
+-=over 4
+-
+-=item WAKEYRING_BUCKET/<hash>/<file>
+-
+-WebAuth keyrings are stored on the wallet server under the directory
+-WAKEYRING_BUCKET as set in the wallet configuration. <hash> is the first
+-two characters of the hex-encoded MD5 hash of the wallet file object name,
+-used to not put too many files in the same directory. <file> is the name
+-of the file object with all characters other than alphanumerics,
+-underscores, and dashes replaced by "%" and the hex code of the character.
+-
+-=back
+-
+-=head1 SEE ALSO
+-
+-Wallet::Config(3), Wallet::Object::Base(3), wallet-backend(8), WebAuth(3)
+-
+-This module is part of the wallet system. The current version is available
+-from L<https://www.eyrie.org/~eagle/software/wallet/>.
+-
+-=head1 AUTHOR
+-
+-Russ Allbery <eagle@eyrie.org>
+-
+-=cut
+Index: wallet/NEWS
+===================================================================
+--- wallet.orig/NEWS 2022-06-18 01:53:50.800081794 +0000
++++ wallet/NEWS 2022-06-18 01:53:50.780080738 +0000
+@@ -1,5 +1,13 @@
+ User-Visible wallet Changes
+
++wallet 1.4-10 (2022-06-17)
++
++ The support for WebAuth keyrings has been removed since it is not
++ supported by Debian.
++
++ There is a critical fix for transition from unencrypted password
++ and file objects to encrypted.
++
+ wallet 1.4-3 (2019-09-30)
+
+ Two new features have been added to wallet with this release.
+Index: wallet/README
+===================================================================
+--- wallet.orig/README 2022-06-18 01:53:50.800081794 +0000
++++ wallet/README 2022-06-18 01:53:50.780080738 +0000
+@@ -42,16 +42,16 @@
+ regexes matching Kerberos principal names, and LDAP attribute checks.
+
+ Currently, the object types supported are simple files, passwords,
+- Kerberos keytabs, WebAuth keyrings, and Duo integrations. By default,
+- whenever a Kerberos keytab object is retrieved from the wallet, the key
+- is changed in the Kerberos KDC and the wallet returns a keytab for the
+- new key. However, a keytab object can also be configured to preserve
+- the existing keys when retrieved. Included in the wallet distribution
+- is a script that can be run via remctl on an MIT Kerberos KDC to extract
+- the existing key for a principal, and the wallet system will use that
+- interface to retrieve the current key if the unchanging flag is set on a
+- Kerberos keytab object for MIT Kerberos. (Heimdal doesn't require any
+- special support.)
++ Kerberos keytabs, and Duo integrations. By default, whenever a
++ Kerberos keytab object is retrieved from the wallet, the key is
++ changed in the Kerberos KDC and the wallet returns a keytab for the
++ new key. However, a keytab object can also be configured to
++ preserve the existing keys when retrieved. Included in the wallet
++ distribution is a script that can be run via remctl on an MIT
++ Kerberos KDC to extract the existing key for a principal, and the
++ wallet system will use that interface to retrieve the current key if
++ the unchanging flag is set on a Kerberos keytab object for MIT
++ Kerberos. (Heimdal doesn't require any special support.)
+
+ REQUIREMENTS
+
+@@ -98,9 +98,6 @@
+ binary that supports the -norandkey option to ktadd. This option is
+ included in MIT Kerberos 1.7 and later.
+
+- The WebAuth keyring object support in the wallet server requires the
+- WebAuth Perl module from WebAuth 4.4.0 or later.
+-
+ The Duo integration object support in the wallet server requires the
+ Net::Duo, JSON, and Perl6::Slurp Perl modules.
+
+Index: wallet/README.md
+===================================================================
+--- wallet.orig/README.md 2022-06-18 01:53:50.800081794 +0000
++++ wallet/README.md 2022-06-18 01:53:50.780080738 +0000
+@@ -43,16 +43,16 @@
+ regexes matching Kerberos principal names, and LDAP attribute checks.
+
+ Currently, the object types supported are simple files, passwords,
+-Kerberos keytabs, WebAuth keyrings, and Duo integrations. By default,
+-whenever a Kerberos keytab object is retrieved from the wallet, the key is
+-changed in the Kerberos KDC and the wallet returns a keytab for the new
+-key. However, a keytab object can also be configured to preserve the
+-existing keys when retrieved. Included in the wallet distribution is a
+-script that can be run via remctl on an MIT Kerberos KDC to extract the
+-existing key for a principal, and the wallet system will use that
+-interface to retrieve the current key if the unchanging flag is set on a
+-Kerberos keytab object for MIT Kerberos. (Heimdal doesn't require any
+-special support.)
++Kerberos keytabs, and Duo integrations. By default, whenever a
++Kerberos keytab object is retrieved from the wallet, the key is
++changed in the Kerberos KDC and the wallet returns a keytab for the
++new key. However, a keytab object can also be configured to preserve
++the existing keys when retrieved. Included in the wallet distribution
++is a script that can be run via remctl on an MIT Kerberos KDC to
++extract the existing key for a principal, and the wallet system will
++use that interface to retrieve the current key if the unchanging flag
++is set on a Kerberos keytab object for MIT Kerberos. (Heimdal doesn't
++require any special support.)
+
+ ## Requirements
+
+@@ -98,9 +98,6 @@
+ supports the `-norandkey` option to `ktadd`. This option is included in
+ MIT Kerberos 1.7 and later.
+
+-The WebAuth keyring object support in the wallet server requires the
+-WebAuth Perl module from WebAuth 4.4.0 or later.
+-
+ The Duo integration object support in the wallet server requires the
+ Net::Duo, JSON, and Perl6::Slurp Perl modules.
+
+Index: wallet/contrib/wallet-rekey-periodic
+===================================================================
+--- wallet.orig/contrib/wallet-rekey-periodic 2022-06-18 01:53:50.800081794 +0000
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,268 +0,0 @@
+-#!/bin/sh
+-#
+-# Rekey all principals on a system at a random but constrained interval.
+-#
+-# This script is a wrapper around wallet-rekey that adds some additional
+-# functionality: rekeying of all keytabs at known locations on the system,
+-# skipping keytabs that are marked unchanging, rekeying any keytabs with DES
+-# keys immediately but otherwise only rekeying once a month based on a random
+-# interval based on the hostname, and cleaning up old keys.
+-#
+-# It's primarily meant to be run daily from cron, but can also be run manually
+-# from the command line to rekey specific keytab files.
+-#
+-# This script assumes Linux, and the test for Heimdal assumes that the
+-# Kerberos clients are installed in /usr/bin. At sites other than Stanford,
+-# change the principal setting near the top of the script to use your local
+-# realm.
+-
+-set -e
+-
+-# Red Hat puts its Kerberos binaries in an odd place. Make sure that we
+-# prefer the system binaries everwhere.
+-PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/kerberos/bin; export PATH
+-
+-# The rekeying interval. Rekeying will be done, on average, every this number
+-# of days.
+-INTERVAL=30
+-
+-# Under normal circumstances, we don't want to rekey every host at the same
+-# time. We therefore run this script daily, but we only do the rekeying if
+-# it's our day to do so.
+-#
+-# For the decision whether we should go on this day, we want to do something
+-# relatively random, but zero-intervention and with no state. We therefore
+-# hash the hostname with MD5 and mod it with $INTERVAL, which gives us a
+-# number between 0 and $INTERVAL - 1. Then, take the mod of the day of the
+-# year. If the result matches the number we got, we rekey.
+-#
+-# We do the check later, since we always want to rekey if we see DES keys.
+-hostnum=$(hostname | md5sum | awk '{print $1}')
+-DAY=$(awk "END { print 0x$hostnum % $INTERVAL }" </dev/null)
+-
+-# Get the principal with which we're going to run all of our commands. We
+-# can't just blindly use the first key in /etc/krb5.keytab, since Heimdal
+-# sometimes reorders the principals in unuseful ways and the host/* key isn't
+-# first.
+-hostname=$(hostname --fqdn)
+-principal="host/${hostname}@stanford.edu"
+-
+-# Do the actual check to see if this is our day to go.
+-is_active_day () {
+- if expr \( $(date +%j) % "$INTERVAL" \) = "$DAY" >/dev/null ; then
+- return 0
+- else
+- return 1
+- fi
+-}
+-
+-# Returns whether the installed Kerberos implementation on the local system is
+-# Heimdal.
+-is_heimdal () {
+- if [ -x '/usr/bin/kgetcred' ] ; then
+- return 0
+- else
+- return 1
+- fi
+-}
+-
+-# Print the list of principals in a keytab.
+-principals () {
+- if is_heimdal ; then
+- ktutil -k "$1" list | awk '{
+- if (FNR > 3) {
+- princ = $3
+- sub(/@.*/, "", princ)
+- print princ
+- }
+- }' | sort -u
+- else
+- klist -k "$1" | awk '{
+- if (FNR > 3) {
+- princ = $2
+- sub(/@.*/, "", princ)
+- print princ
+- }
+- }' | sort -u
+- fi
+-}
+-
+-# Run a command under k5start using the host/* principal for the current
+-# hostname as the authentication credentials.
+-run_k5start () {
+- k5start -qf /etc/krb5.keytab "$principal" -- "$@"
+-}
+-
+-# Check all of the principals in a keytab and see if any of them are
+-# unchanging. If any are, we skip rekeying this keytab, since otherwise we're
+-# going to accumulate multiple copies of the same key and the cleanup
+-# functions won't remove the excess keys.
+-is_unchanging () {
+- princs=$(principals "$1")
+- for princ in $princs ; do
+- if run_k5start wallet show keytab "$princ" 2>&1 \
+- | grep -q 'Flags: unchanging' ; then
+- return 0
+- fi
+- done
+- return 1
+-}
+-
+-# Check whether any of the principals in this keytab have DES keys. This is a
+-# bit complicated, since we don't want to trigger this if there are DES keys
+-# but ones with old kvnos.
+-#
+-# We get a list of all the unique kvnos in the file, and then a list of all
+-# the unique kvnos of DES keys in the file. If those lists match, we consider
+-# this a DES keytab; if not, there's at least one kvno with non-DES keys, so
+-# we consider this a non-DES keytab.
+-is_des () {
+- if is_heimdal ; then
+- all=$(ktutil -k "$1" list | sed '1,3d' | awk '{print $1}' | sort -nu)
+- des=$(ktutil -k "$1" list | grep des-cbc-crc | awk '{print $1}' \
+- | sort -nu)
+- else
+- all=$(klist -k "$1" | sed '1,3d' | awk '{print $1}' | sort -nu)
+- des=$(klist -ke "$1" | egrep '\(DES cbc|des-cbc-crc' \
+- | awk '{print $1}' | sort -nu)
+- fi
+- if [ "$all" = "$des" ] ; then
+- return 0
+- else
+- return 1
+- fi
+-}
+-
+-# Rekey the given keytab file if it exists, this is either the active day or
+-# the keytab contains DES keys, and it isn't unchanging. On Heimdal, we'll
+-# also purge old keys. We can't do this on MIT because the kadmin routine
+-# that purges old keys requires admin authentication.
+-rekey () {
+- if [ -f "$1" ] ; then
+- if is_des "$1" || is_active_day ; then
+- if ! is_unchanging "$1" ; then
+- if is_heimdal ; then
+- ktutil -k "$1" purge
+- fi
+- run_k5start wallet-rekey "$1"
+- fi
+- fi
+- fi
+-}
+-
+-# The default action is to rekey the host keytab, the WebAuth keytab, and any
+-# keytabs found in /etc/keytabs/*. But if we're given keytabs on the command
+-# line, we'll rekey those instead. (This won't generally be used since we're
+-# installed as a cron job.)
+-if [ -z "$1" ] ; then
+- for file in /etc/webauth/keytab /etc/keytabs/* /etc/krb5.keytab ; do
+- rekey "$file"
+- done
+-else
+- for file in "$@" ; do
+- rekey "$file"
+- done
+-fi
+-
+-# Documentation. Use a hack to hide this from the shell. Because of the
+-# above exit line, this should never be executed.
+-DOCS=<<__END_OF_DOCS__
+-
+-=for stopwords
+-Allbery DES Heimdal hostname keytab keytabs ktutil rekey rekeyable
+-rekeying wallet-rekey wallet-rekey-periodic SPDX-License-Identifier MIT
+-
+-=head1 NAME
+-
+-wallet-rekey-periodic - Periodically rekey all system keytabs
+-
+-=head1 SYNOPSIS
+-
+-B<wallet-rekey-periodic> [I<keytab> ...]
+-
+-=head1 DESCRIPTION
+-
+-B<wallet-rekey-periodic> is a wrapper around wallet-rekey that adds some
+-additional functionality: rekeying of all keytabs at known locations on
+-the system, skipping keytabs that are marked unchanging, rekeying any
+-keytabs with DES keys immediately but otherwise only rekeying once a month
+-based on a random interval based on the hostname, and cleaning up old
+-keys.
+-
+-It's primarily meant to be run daily from cron, but can also be run
+-manually from the command line to rekey specific keytab files.
+-
+-B<wallet-rekey-periodic> will, for each keytab, find a list of all
+-principals in that keytab and see if any of them still have DES keys. If
+-so, it will always attempt to rekey that keytab. If not, it will only do
+-so, for a given system, once every 30 days (based on a hash of the
+-hostname). It will also always skip keytabs that contain any principals
+-that wallet says are unchanging, since otherwise the current wallet-rekey
+-implementation will duplicate the existing keys.
+-
+-On Heimdal systems, this command will remove keys older than a week before
+-rekeying the keytab. This relies on B<ktutil> functionality that's
+-available only in Heimdal, so MIT Kerberos keytabs will slowly grow unless
+-they're manually pruned. This will be fixed in a later release of
+-B<wallet-rekey>.
+-
+-If no keytabs are given on the command line, B<wallet-rekey-periodic> will
+-rekey a set of system keytabs described below under L</FILES>. Otherwise,
+-it will rekey the keytabs given.
+-
+-=head1 FILES
+-
+-=over 4
+-
+-=item F</etc/keytabs/*>
+-
+-=item F</etc/krb5.keytab>
+-
+-=item F</etc/webauth/keytab>
+-
+-The default list of locations checked for rekeyable keytabs. If run with
+-no command-line arguments, B<wallet-rekey-periodic> will try to rekey
+-every principal in each keytab found at any of these paths.
+-
+-=back
+-
+-=head1 AUTHOR
+-
+-Russ Allbery <eagle@eyrie.org>
+-
+-=head1 COPYRIGHT AND LICENSE
+-
+-Copyright 2013-2014 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.
+-
+-SPDX-License-Identifier: MIT
+-
+-=head1 SEE ALSO
+-
+-ktutil(8), wallet(1), wallet-rekey(1)
+-
+-=cut
+-
+-__END_OF_DOCS__
+-
+-# Local Variables:
+-# copyright-at-end-flag: t
+-# End:
+Index: wallet/perl/lib/Wallet/Config.pm
+===================================================================
+--- wallet.orig/perl/lib/Wallet/Config.pm 2022-06-18 01:53:50.800081794 +0000
++++ wallet/perl/lib/Wallet/Config.pm 2022-06-18 01:53:50.784080949 +0000
+@@ -26,7 +26,7 @@
+ DBI DSN SQLite subdirectories KEYTAB keytab kadmind KDC add-ons kadmin DNS
+ SRV kadmin keytabs remctl backend lowercased NETDB ACL NetDB unscoped
+ usernames rekey hostnames Allbery wallet-backend keytab-backend Heimdal
+-rekeys WebAuth WEBAUTH keyring LDAP DN GSS-API integrations msktutil CN DIT
++rekeys LDAP DN GSS-API integrations msktutil CN DIT
+
+ =head1 SYNOPSIS
+
+@@ -645,67 +645,6 @@
+
+ =back
+
+-=head1 WEBAUTH KEYRING OBJECT CONFIGURATION
+-
+-These configuration variables only need to be set if you intend to use the
+-C<wakeyring> object type (the Wallet::Object::WAKeyring class).
+-
+-=over 4
+-
+-=item WAKEYRING_BUCKET
+-
+-The directory into which to store WebAuth keyring objects. WebAuth
+-keyring objects will be stored in subdirectories of this directory. See
+-L<Wallet::Object::WAKeyring> for the full details of the naming scheme.
+-This directory must be writable by the wallet server and the wallet server
+-must be able to create subdirectories of it.
+-
+-WAKEYRING_BUCKET must be set to use WebAuth keyring objects.
+-
+-=cut
+-
+-our $WAKEYRING_BUCKET;
+-
+-=item WAKEYRING_REKEY_INTERVAL
+-
+-The interval, in seconds, at which new keys are generated in a keyring.
+-The object implementation will try to arrange for there to be keys added
+-to the keyring separated by this interval.
+-
+-It's useful to provide some interval to install the keyring everywhere
+-that it's used before the key becomes inactive. Every keyring will
+-therefore normally have at least three keys: one that's currently active,
+-one that becomes valid in the future but less than
+-WAKEYRING_REKEY_INTERVAL from now, and one that becomes valid between one
+-and two of those intervals into the future. This means that one has twice
+-this interval to distribute the keyring everywhere it is used.
+-
+-Internally, this is implemented by adding a new key that becomes valid in
+-twice this interval from the current time if the newest key becomes valid
+-at or less than this interval in the future.
+-
+-The default value is 60 * 60 * 24 (one day).
+-
+-=cut
+-
+-our $WAKEYRING_REKEY_INTERVAL = 60 * 60 * 24;
+-
+-=item WAKEYRING_PURGE_INTERVAL
+-
+-The interval, in seconds, from the key creation date after which keys are
+-removed from the keyring. This is used to clean up old keys and finish
+-key rotation. Keys won't be removed unless there are more than three keys
+-in the keyring to try to keep a misconfiguration from removing all valid
+-keys.
+-
+-The default value is 60 * 60 * 24 * 90 (90 days).
+-
+-=cut
+-
+-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
+@@ -940,7 +879,7 @@
+ sub default_owner {
+ my ($type, $name) = @_;
+ my %allowed = map { $_ => 1 }
+- qw(HTTP cifs host imap ldap nfs pop sieve smtp webauth);
++ qw(HTTP cifs host imap ldap nfs pop sieve smtp);
+ my $realm = 'example.com';
+ return unless $type eq 'keytab';
+ return unless $name =~ m%/%;
+@@ -997,7 +936,7 @@
+ sub verify_name {
+ my ($type, $name, $user) = @_;
+ my %host_based = map { $_ => 1 }
+- qw(HTTP cifs host imap ldap nfs pop sieve smtp webauth);
++ qw(HTTP cifs host imap ldap nfs pop sieve smtp);
+ return unless $type eq 'keytab';
+ return unless $name =~ m%/%;
+ my ($service, $instance) = split ('/', $name, 2);
+@@ -1029,7 +968,7 @@
+ sub is_for_host {
+ my ($type, $name, $hostname) = @_;
+ my %host_based = map { $_ => 1 }
+- qw(HTTP cifs host imap ldap nfs pop sieve smtp webauth);
++ qw(HTTP cifs host imap ldap nfs pop sieve smtp);
+ return 0 unless $type eq 'keytab';
+ return 0 unless $name =~ m%/%;
+ my ($service, $instance) = split ('/', $name, 2);
+Index: wallet/perl/t/object/wa-keyring.t
+===================================================================
+--- wallet.orig/perl/t/object/wa-keyring.t 2022-06-18 01:53:50.800081794 +0000
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,183 +0,0 @@
+-#!/usr/bin/perl
+-#
+-# Tests for the WebAuth keyring object implementation.
+-#
+-# Written by Russ Allbery <eagle@eyrie.org>
+-# Copyright 2013-2014
+-# The Board of Trustees of the Leland Stanford Junior University
+-#
+-# SPDX-License-Identifier: MIT
+-
+-use strict;
+-use warnings;
+-
+-use Test::More;
+-
+-BEGIN {
+- eval 'use WebAuth 3.06 qw(WA_KEY_AES WA_AES_128)';
+- plan skip_all => 'WebAuth 3.06 required for testing wa-keyring'
+- if $@;
+-}
+-
+-use WebAuth::Key 1.01 ();
+-use WebAuth::Keyring 1.02 ();
+-
+-BEGIN {
+- plan tests => 68;
+- use_ok('Wallet::Admin');
+- use_ok('Wallet::Config');
+- use_ok('Wallet::Object::WAKeyring');
+-}
+-
+-use lib 't/lib';
+-use Util;
+-
+-# Some global defaults to use.
+-my $user = 'admin@EXAMPLE.COM';
+-my $host = 'localhost';
+-my @trace = ($user, $host, time);
+-
+-# Flush all output immediately.
+-$| = 1;
+-
+-# Use Wallet::Admin to set up the database.
+-system ('rm -rf test-keyrings') == 0 or die "cannot remove test-keyrings\n";
+-db_setup;
+-my $admin = eval { Wallet::Admin->new };
+-is ($@, '', 'Database connection succeeded');
+-is ($admin->reinitialize ($user), 1, 'Database initialization succeeded');
+-my $schema = $admin->schema;
+-
+-# Create a WebAuth context to use.
+-my $wa = WebAuth->new;
+-
+-# Test error handling in the absence of configuration.
+-my $object = eval {
+- Wallet::Object::WAKeyring->create ('wa-keyring', 'test', $schema, @trace)
+- };
+-ok (defined ($object), 'Creating a basic WebAuth keyring object succeeds');
+-ok ($object->isa ('Wallet::Object::WAKeyring'), ' and is the right class');
+-is ($object->get (@trace), undef, ' and get fails');
+-is ($object->error, 'WebAuth keyring support not configured',
+- ' with the right error');
+-is ($object->store (@trace), undef, ' and store fails');
+-is ($object->error, 'WebAuth keyring support not configured',
+- ' with the right error');
+-is ($object->destroy (@trace), 1, ' but destroy succeeds');
+-
+-# Set up our configuration.
+-mkdir 'test-keyrings' or die "cannot create test-keyrings: $!\n";
+-$Wallet::Config::WAKEYRING_BUCKET = 'test-keyrings';
+-
+-# Okay, now we can test. First, the basic object without store.
+-$object = eval {
+- Wallet::Object::WAKeyring->create ('wa-keyring', 'test', $schema, @trace)
+- };
+-ok (defined ($object), 'Creating a basic WebAuth keyring object succeeds');
+-ok ($object->isa ('Wallet::Object::WAKeyring'), ' and is the right class');
+-my $data = $object->get (@trace);
+-ok ($data, ' and get succeeds');
+-my $keyring = WebAuth::Keyring->decode ($wa, $data);
+-ok ($keyring->isa ('WebAuth::Keyring'), ' and resulting keyring decodes');
+-my @entries = $keyring->entries;
+-is (scalar (@entries), 3, ' and has three entries');
+-is ($entries[0]->creation, 0, 'First has good creation');
+-is ($entries[0]->key->type, WA_KEY_AES, ' and key type');
+-is ($entries[0]->key->length, WA_AES_128, ' and key length');
+-is ($entries[0]->valid_after, 0, ' and validity');
+-ok ((time - $entries[1]->creation) < 2, 'Second has good creation');
+-is ($entries[1]->key->type, WA_KEY_AES, ' and key type');
+-is ($entries[1]->key->length, WA_AES_128, ' and key length');
+-ok (($entries[1]->valid_after - time) <= 60 * 60 * 24,
+- ' and validity (upper)');
+-ok (($entries[1]->valid_after - time) > 60 * 60 * 24 - 2,
+- ' and validity (lower)');
+-ok ((time - $entries[2]->creation) < 2, 'Third has good creation');
+-is ($entries[2]->key->type, WA_KEY_AES, ' and key type');
+-is ($entries[2]->key->length, WA_AES_128, ' and key length');
+-ok (($entries[2]->valid_after - time) <= 2 * 60 * 60 * 24,
+- ' and validity (upper)');
+-ok (($entries[2]->valid_after - time) > 2 * 60 * 60 * 24 - 2,
+- ' and validity (lower)');
+-my $data2 = $object->get (@trace);
+-is ($data2, $data, 'Getting the object again returns the same data');
+-is ($object->error, undef, ' with no error');
+-is ($object->destroy (@trace), 1, 'Destroying the object succeeds');
+-
+-# Now store something and be sure that we get something reasonable.
+-$object = eval {
+- Wallet::Object::WAKeyring->create ('wa-keyring', 'test', $schema, @trace)
+- };
+-ok (defined ($object), 'Recreating the object succeeds');
+-my $key = WebAuth::Key->new ($wa, WA_KEY_AES, WA_AES_128);
+-$keyring = WebAuth::Keyring->new ($wa, $key);
+-$data = $keyring->encode;
+-is ($object->store ($data, @trace), 1, ' and storing data in it succeeds');
+-ok (-d 'test-keyrings/09', ' and the hash bucket was created');
+-ok (-f 'test-keyrings/09/test', ' and the file exists');
+-is (contents ('test-keyrings/09/test'), $data, ' with the right contents');
+-$data = $object->get (@trace);
+-$keyring = WebAuth::Keyring->decode ($wa, $data);
+-ok ($keyring->isa ('WebAuth::Keyring'), ' and get returns a valid keyring');
+-@entries = $keyring->entries;
+-is (scalar (@entries), 2, ' and has three entries');
+-is ($entries[0]->creation, 0, 'First has good creation');
+-is ($entries[0]->key->type, WA_KEY_AES, ' and key type');
+-is ($entries[0]->key->length, WA_AES_128, ' and key length');
+-is ($entries[0]->valid_after, 0, ' and validity');
+-is ($entries[0]->key->data, $key->data, ' and matches the original key');
+-ok ((time - $entries[1]->creation) < 2, 'Second has good creation');
+-is ($entries[1]->key->type, WA_KEY_AES, ' and key type');
+-is ($entries[1]->key->length, WA_AES_128, ' and key length');
+-ok (($entries[1]->valid_after - time) <= 2 * 60 * 60 * 24,
+- ' and validity (upper)');
+-ok (($entries[1]->valid_after - time) > 2 * 60 * 60 * 24 - 2,
+- ' and validity (lower)');
+-
+-# Test pruning. Add another old key and a couple of more current keys to the
+-# current keyring.
+-$key = WebAuth::Key->new ($wa, WA_KEY_AES, WA_AES_128);
+-$keyring->add (0, 0, $key);
+-$key = WebAuth::Key->new ($wa, WA_KEY_AES, WA_AES_128);
+-$keyring->add (time - 24 * 60 * 60, time - 24 * 60 * 60, $key);
+-$key = WebAuth::Key->new ($wa, WA_KEY_AES, WA_AES_128);
+-$keyring->add (time, time, $key);
+-$data = $keyring->encode;
+-is ($object->store ($data, @trace), 1, 'Storing modified keyring succeeds');
+-$data = $object->get (@trace);
+-$keyring = WebAuth::Keyring->decode ($wa, $data);
+-ok ($keyring->isa ('WebAuth::Keyring'), ' and get returns a valid keyring');
+-@entries = $keyring->entries;
+-is (scalar (@entries), 3, ' and has three entries');
+-ok ((time - $entries[0]->creation) < 2, 'First has good creation');
+-ok (($entries[0]->valid_after - time) <= 2 * 60 * 60 * 24,
+- ' and validity (upper)');
+-ok (($entries[0]->valid_after - time) > 2 * 60 * 60 * 24 - 2,
+- ' and validity (lower)');
+-ok ((time - $entries[1]->creation) < 24 * 60 * 60 + 2,
+- 'Second has good creation');
+-ok ((time - $entries[1]->valid_after) <= 60 * 60 * 24 + 2,
+- ' and validity');
+-ok ((time - $entries[2]->creation) < 2, 'Third has good creation');
+-ok ((time - $entries[2]->valid_after) < 2, ' and validity');
+-is ($object->destroy (@trace), 1, 'Destroying the object succeeds');
+-
+-# Test error handling in the file store.
+-system ('rm -r test-keyrings') == 0 or die "cannot remove test-keyrings\n";
+-$object = eval {
+- Wallet::Object::WAKeyring->create ('wa-keyring', 'test', $schema, @trace)
+- };
+-ok (defined ($object), 'Recreating the object succeeds');
+-is ($object->get (@trace), undef, ' but retrieving it fails');
+-like ($object->error, qr/^cannot create keyring bucket 09: /,
+- ' with the right error');
+-is ($object->store ("foo\n", @trace), undef, ' and store fails');
+-like ($object->error, qr/^cannot create keyring bucket 09: /,
+- ' with the right error');
+-is ($object->destroy (@trace), 1, ' but destroying the object succeeds');
+-
+-# Clean up.
+-$admin->destroy;
+-END {
+- unlink ('wallet-db');
+-}
+Index: wallet/contrib/wallet-rekey-periodic.8
+===================================================================
+--- wallet.orig/contrib/wallet-rekey-periodic.8 2022-06-18 01:53:50.800081794 +0000
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,213 +0,0 @@
+-.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
+-.\"
+-.\" Standard preamble:
+-.\" ========================================================================
+-.de Sp \" Vertical space (when we can't use .PP)
+-.if t .sp .5v
+-.if n .sp
+-..
+-.de Vb \" Begin verbatim text
+-.ft CW
+-.nf
+-.ne \\$1
+-..
+-.de Ve \" End verbatim text
+-.ft R
+-.fi
+-..
+-.\" Set up some character translations and predefined strings. \*(-- will
+-.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+-.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+-.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+-.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+-.\" nothing in troff, for use with C<>.
+-.tr \(*W-
+-.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+-.ie n \{\
+-. ds -- \(*W-
+-. ds PI pi
+-. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+-. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+-. ds L" ""
+-. ds R" ""
+-. ds C` ""
+-. ds C' ""
+-'br\}
+-.el\{\
+-. ds -- \|\(em\|
+-. ds PI \(*p
+-. ds L" ``
+-. ds R" ''
+-. ds C`
+-. ds C'
+-'br\}
+-.\"
+-.\" Escape single quotes in literal strings from groff's Unicode transform.
+-.ie \n(.g .ds Aq \(aq
+-.el .ds Aq '
+-.\"
+-.\" If the F register is >0, we'll generate index entries on stderr for
+-.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+-.\" entries marked with X<> in POD. Of course, you'll have to process the
+-.\" output yourself in some meaningful fashion.
+-.\"
+-.\" Avoid warning from groff about undefined register 'F'.
+-.de IX
+-..
+-.if !\nF .nr F 0
+-.if \nF>0 \{\
+-. de IX
+-. tm Index:\\$1\t\\n%\t"\\$2"
+-..
+-. if !\nF==2 \{\
+-. nr % 0
+-. nr F 2
+-. \}
+-.\}
+-.\"
+-.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+-.\" Fear. Run. Save yourself. No user-serviceable parts.
+-. \" fudge factors for nroff and troff
+-.if n \{\
+-. ds #H 0
+-. ds #V .8m
+-. ds #F .3m
+-. ds #[ \f1
+-. ds #] \fP
+-.\}
+-.if t \{\
+-. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+-. ds #V .6m
+-. ds #F 0
+-. ds #[ \&
+-. ds #] \&
+-.\}
+-. \" simple accents for nroff and troff
+-.if n \{\
+-. ds ' \&
+-. ds ` \&
+-. ds ^ \&
+-. ds , \&
+-. ds ~ ~
+-. ds /
+-.\}
+-.if t \{\
+-. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+-. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+-. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+-. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+-. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+-. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+-.\}
+-. \" troff and (daisy-wheel) nroff accents
+-.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+-.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+-.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+-.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+-.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+-.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+-.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+-.ds ae a\h'-(\w'a'u*4/10)'e
+-.ds Ae A\h'-(\w'A'u*4/10)'E
+-. \" corrections for vroff
+-.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+-.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+-. \" for low resolution devices (crt and lpr)
+-.if \n(.H>23 .if \n(.V>19 \
+-\{\
+-. ds : e
+-. ds 8 ss
+-. ds o a
+-. ds d- d\h'-1'\(ga
+-. ds D- D\h'-1'\(hy
+-. ds th \o'bp'
+-. ds Th \o'LP'
+-. ds ae ae
+-. ds Ae AE
+-.\}
+-.rm #[ #] #H #V #F C
+-.\" ========================================================================
+-.\"
+-.IX Title "WALLET-REKEY-PERIODIC 8"
+-.TH WALLET-REKEY-PERIODIC 8 "2018-06-04" "1.4" "wallet"
+-.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+-.\" way too many mistakes in technical documents.
+-.if n .ad l
+-.nh
+-.SH "NAME"
+-wallet\-rekey\-periodic \- Periodically rekey all system keytabs
+-.SH "SYNOPSIS"
+-.IX Header "SYNOPSIS"
+-\&\fBwallet-rekey-periodic\fR [\fIkeytab\fR ...]
+-.SH "DESCRIPTION"
+-.IX Header "DESCRIPTION"
+-\&\fBwallet-rekey-periodic\fR is a wrapper around wallet-rekey that adds some
+-additional functionality: rekeying of all keytabs at known locations on
+-the system, skipping keytabs that are marked unchanging, rekeying any
+-keytabs with \s-1DES\s0 keys immediately but otherwise only rekeying once a month
+-based on a random interval based on the hostname, and cleaning up old
+-keys.
+-.PP
+-It's primarily meant to be run daily from cron, but can also be run
+-manually from the command line to rekey specific keytab files.
+-.PP
+-\&\fBwallet-rekey-periodic\fR will, for each keytab, find a list of all
+-principals in that keytab and see if any of them still have \s-1DES\s0 keys. If
+-so, it will always attempt to rekey that keytab. If not, it will only do
+-so, for a given system, once every 30 days (based on a hash of the
+-hostname). It will also always skip keytabs that contain any principals
+-that wallet says are unchanging, since otherwise the current wallet-rekey
+-implementation will duplicate the existing keys.
+-.PP
+-On Heimdal systems, this command will remove keys older than a week before
+-rekeying the keytab. This relies on \fBktutil\fR functionality that's
+-available only in Heimdal, so \s-1MIT\s0 Kerberos keytabs will slowly grow unless
+-they're manually pruned. This will be fixed in a later release of
+-\&\fBwallet-rekey\fR.
+-.PP
+-If no keytabs are given on the command line, \fBwallet-rekey-periodic\fR will
+-rekey a set of system keytabs described below under \*(L"\s-1FILES\*(R"\s0. Otherwise,
+-it will rekey the keytabs given.
+-.SH "FILES"
+-.IX Header "FILES"
+-.IP "\fI/etc/keytabs/*\fR" 4
+-.IX Item "/etc/keytabs/*"
+-.PD 0
+-.IP "\fI/etc/krb5.keytab\fR" 4
+-.IX Item "/etc/krb5.keytab"
+-.IP "\fI/etc/webauth/keytab\fR" 4
+-.IX Item "/etc/webauth/keytab"
+-.PD
+-The default list of locations checked for rekeyable keytabs. If run with
+-no command-line arguments, \fBwallet-rekey-periodic\fR will try to rekey
+-every principal in each keytab found at any of these paths.
+-.SH "AUTHOR"
+-.IX Header "AUTHOR"
+-Russ Allbery <eagle@eyrie.org>
+-.SH "COPYRIGHT AND LICENSE"
+-.IX Header "COPYRIGHT AND LICENSE"
+-Copyright 2013\-2014 The Board of Trustees of the Leland Stanford Junior
+-University
+-.PP
+-Permission is hereby granted, free of charge, to any person obtaining a
+-copy of this software and associated documentation files (the \*(L"Software\*(R"),
+-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:
+-.PP
+-The above copyright notice and this permission notice shall be included in
+-all copies or substantial portions of the Software.
+-.PP
+-\&\s-1THE SOFTWARE IS PROVIDED \*(L"AS IS\*(R", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\s0 \s-1IN 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.\s0
+-.PP
+-SPDX-License-Identifier: \s-1MIT\s0
+-.SH "SEE ALSO"
+-.IX Header "SEE ALSO"
+-\&\fIktutil\fR\|(8), \fIwallet\fR\|(1), \fIwallet\-rekey\fR\|(1)
+Index: wallet/contrib/wallet-summary
+===================================================================
+--- wallet.orig/contrib/wallet-summary 2022-06-18 01:53:50.800081794 +0000
++++ wallet/contrib/wallet-summary 2022-06-18 01:53:50.784080949 +0000
+@@ -41,7 +41,6 @@
+ [qr(^pop/), 'pop/*', 'Kerberized POP'],
+ [qr(^sieve/), 'sieve/*', 'Sieve mail sorting'],
+ [qr(^smtp/), 'smtp/*', 'SMTP'],
+- [qr(^webauth/), 'webauth/*', 'WebAuth v3'],
+ [qr(^service/), 'service/*', 'Service principals']);
+
+ ##############################################################################
+Index: wallet/contrib/used-principals
+===================================================================
+--- wallet.orig/contrib/used-principals 2022-06-18 01:53:50.800081794 +0000
++++ wallet/contrib/used-principals 2022-06-18 01:53:50.784080949 +0000
+@@ -18,7 +18,7 @@
+ # appended.
+ our %HOST_BASED = map { $_ => 1 }
+ qw(HTTP afpserver cifs ftp host ident imap ldap nfs pop sieve smtp
+- uniengd webauth);
++ uniengd);
+
+ # Parse command-line options.
+ my ($count, $help, $k4, $principals);
+Index: wallet/perl/t/policy/stanford.t
+===================================================================
+--- wallet.orig/perl/t/policy/stanford.t 2022-06-18 01:53:50.800081794 +0000
++++ wallet/perl/t/policy/stanford.t 2022-06-18 01:53:50.784080949 +0000
+@@ -211,15 +211,6 @@
+ ],
+ '...and when netdb ACL already exists'
+ );
+- is_deeply(
+- [default_owner('keytab', 'webauth/foo.stanford.edu')],
+- [
+- 'host/foo.stanford.edu',
+- ['netdb-root', 'foo.stanford.edu'],
+- ['krb5', 'host/foo.stanford.edu@stanford.edu']
+- ],
+- '...and when netdb-root ACL already exists'
+- );
+
+ # Now with a root instance.
+ local $ENV{REMOTE_USER} = 'admin/root@stanford.edu';
+@@ -241,15 +232,6 @@
+ ],
+ '...and when netdb ACL already exists'
+ );
+- is_deeply(
+- [default_owner('keytab', 'webauth/foo.stanford.edu')],
+- [
+- 'host/foo.stanford.edu',
+- ['netdb-root', 'foo.stanford.edu'],
+- ['krb5', 'host/foo.stanford.edu@stanford.edu']
+- ],
+- '...and when netdb-root ACL already exists'
+- );
+
+ # Check for a type that isn't host-based.
+ is(
+Index: wallet/perl/Build.PL
+===================================================================
+--- wallet.orig/perl/Build.PL 2022-06-18 01:53:50.800081794 +0000
++++ wallet/perl/Build.PL 2022-06-18 01:53:50.784080949 +0000
+@@ -46,7 +46,6 @@
+ 'Net::Duo' => 0,
+ 'Net::LDAP' => 0,
+ 'Net::Remctl' => 0,
+- WebAuth => 0,
+ },
+ test_requires => {
+ 'Crypt::GeneratePassword' => 0,
+Index: wallet/docs/metadata/description
+===================================================================
+--- wallet.orig/docs/metadata/description 2022-06-18 01:53:44.683758725 +0000
++++ wallet/docs/metadata/description 2022-06-18 01:54:27.806036564 +0000
+@@ -19,7 +19,7 @@
+ regexes matching Kerberos principal names, and LDAP attribute checks.
+
+ Currently, the object types supported are simple files, passwords,
+-Kerberos keytabs, WebAuth keyrings, and Duo integrations. By default,
++Kerberos keytabs, and Duo integrations. By default,
+ whenever a Kerberos keytab object is retrieved from the wallet, the key is
+ changed in the Kerberos KDC and the wallet returns a keytab for the new
+ key. However, a keytab object can also be configured to preserve the
+Index: wallet/docs/metadata/requirements
+===================================================================
+--- wallet.orig/docs/metadata/requirements 2022-06-18 01:20:49.283265080 +0000
++++ wallet/docs/metadata/requirements 2022-06-18 01:56:13.399614445 +0000
+@@ -40,9 +40,6 @@
+ supports the `-norandkey` option to `ktadd`. This option is included in
+ MIT Kerberos 1.7 and later.
+
+-The WebAuth keyring object support in the wallet server requires the
+-WebAuth Perl module from WebAuth 4.4.0 or later.
+-
+ The Duo integration object support in the wallet server requires the
+ Net::Duo, JSON, and Perl6::Slurp Perl modules.
+
diff --git a/debian/patches/0021-spx-test.patch b/debian/patches/0021-spx-test.patch
new file mode 100644
index 0000000..82aa5d3
--- /dev/null
+++ b/debian/patches/0021-spx-test.patch
@@ -0,0 +1,12 @@
+Index: wallet/tests/docs/spdx-license-t
+===================================================================
+--- wallet.orig/tests/docs/spdx-license-t 2022-06-05 23:25:17.526523890 +0000
++++ wallet/tests/docs/spdx-license-t 2022-06-05 23:26:57.183817283 +0000
+@@ -53,6 +53,7 @@
+ qr{ [.] output \z }xms, # Test data
+ );
+ my @IGNORE_PATHS = (
++ qr{ \A configure~ }xms, # Build artifact
+ qr{ \A debian/ }xms, # Found in debian/* branches
+ qr{ \A docs/metadata/ }xms, # Package license should be fine
+ qr{ \A docs/protocol[.](html|txt) \z }xms, # Generated by xml2rfc
diff --git a/debian/patches/0022-password-double-encryption.patch b/debian/patches/0022-password-double-encryption.patch
new file mode 100644
index 0000000..e183964
--- /dev/null
+++ b/debian/patches/0022-password-double-encryption.patch
@@ -0,0 +1,12 @@
+--- a/perl/lib/Wallet/Object/Password.pm
++++ b/perl/lib/Wallet/Object/Password.pm
+@@ -187,8 +187,7 @@ sub retrieve {
+ if ($Wallet::Config::LDAP_SECRET) {
+ my $undata = Wallet::Object::File->string_decrypt($data);
+ if ($undata eq $data) {
+- my $endata = Wallet::Object::File->file_encrypt($data);
+- $self->_write_pw_file($path, $endata);
++ $self->_write_pw_file($path, $data);
+ $self->log_action ($operation, $user, $host, $time);
+ }
+ $data = $undata;
diff --git a/debian/patches/0023-ldap-attr-filter.patch b/debian/patches/0023-ldap-attr-filter.patch
new file mode 100644
index 0000000..b204d36
--- /dev/null
+++ b/debian/patches/0023-ldap-attr-filter.patch
@@ -0,0 +1,183 @@
+Index: wallet/perl/lib/Wallet/ACL/LDAP/Attribute.pm
+===================================================================
+--- wallet.orig/perl/lib/Wallet/ACL/LDAP/Attribute.pm 2022-11-18 08:01:14.615451075 +0000
++++ wallet/perl/lib/Wallet/ACL/LDAP/Attribute.pm 2022-11-18 08:03:02.096649951 +0000
+@@ -62,10 +62,9 @@
+ return $self;
+ }
+
+-# Check whether a given principal has the required LDAP attribute. We first
+-# map the principal to a DN by doing a search for that principal (and bailing
+-# if we get more than one entry). Then, we do a compare to see if that DN has
+-# the desired attribute and value.
++# Check whether a given principal has access to the wallet object
++# using an LDAP search using a filter consisting of the principal
++# and the ldap-attr filter.
+ #
+ # If the ldap_map_principal sub is defined in Wallet::Config, call it on the
+ # principal first to map it to the value for which we'll search.
+@@ -75,18 +74,29 @@
+ sub check {
+ my ($self, $principal, $acl) = @_;
+ undef $self->{error};
+- unless ($principal) {
++ if (!$principal) {
+ $self->error ('no principal specified');
+ return;
+ }
+- my ($attr, $value);
+- if ($acl) {
+- ($attr, $value) = split ('=', $acl, 2);
++
++ if (!$acl) {
++ $self->error ('no ACL specified');
++ return;
++ }
++ if ($acl !~ /=/xms) {
++ $self->error ('Malformed LDAP filter, no equal sign present');
++ return;
+ }
+- unless (defined ($attr) and defined ($value)) {
+- $self->error ('malformed ldap-attr ACL');
++ my $lcnt = $acl =~ tr/\(//;
++ my $rcnt = $acl =~ tr/\)//;
++ if ($lcnt != $rcnt) {
++ $self->error ('Malformed LDAP filter, parenthesis mismatch');
+ return;
+ }
++ my $attr_filter = $acl;
++ if ($attr_filter !~ /^\(/xms) {
++ $attr_filter = "($attr_filter)";
++ }
+ my $ldap = $self->{ldap};
+
+ # Map the principal name to an attribute value for our search if we're
+@@ -99,38 +109,29 @@
+ }
+ }
+
+- # Now, map the user to a DN by doing a search.
+- my $entry;
++ # Now search for one, and only one, matching entry
++ my $found;
++ my $fattr = $Wallet::Config::LDAP_FILTER_ATTR || 'krb5PrincipalName';
++ my $filter = "(&($fattr=$principal)$attr_filter)";
++ my $base = $Wallet::Config::LDAP_BASE;
++ my @options = (base => $base, filter => $filter, attrs => [ 'dn' ]);
+ eval {
+- my $fattr = $Wallet::Config::LDAP_FILTER_ATTR || 'krb5PrincipalName';
+- my $filter = "($fattr=$principal)";
+- my $base = $Wallet::Config::LDAP_BASE;
+- my @options = (base => $base, filter => $filter, attrs => [ 'dn' ]);
+ my $search = $ldap->search (@options);
+ if ($search->count == 1) {
+- $entry = $search->pop_entry;
++ $found = 1;
+ } elsif ($search->count > 1) {
+ die $search->count . " LDAP entries found for $principal";
+ }
+ };
+ if ($@) {
+- $self->error ("cannot search for $principal in LDAP: $@");
++ $self->error ("search for $attr_filter failed in LDAP: $@");
+ return;
+ }
+- return 0 unless $entry;
+-
+- # We have a user entry. We can now check whether that user has the
+- # desired attribute and value.
+- my $result;
+- eval {
+- my $mesg = $ldap->compare ($entry, attr => $attr, value => $value);
+- $result = $mesg->code;
+- };
+- if ($@) {
+- $self->error ("cannot check LDAP attribute $attr for $principal: $@");
+- return;
++ if ($found) {
++ return 1;
+ }
+- return ($result == LDAP_COMPARE_TRUE) ? 1 : 0;
++
++ return;
+ }
+
+ 1;
+@@ -160,12 +161,13 @@
+
+ =head1 DESCRIPTION
+
+-Wallet::ACL::LDAP::Attribute checks whether the LDAP record for the entry
+-corresponding to a principal contains an attribute with a particular
+-value. It is used to verify ACL lines of type C<ldap-attr>. The value of
+-such an ACL is an attribute followed by an equal sign and a value, and the
+-ACL grants access to a given principal if and only if the LDAP entry for
+-that principal has that attribute set to that value.
++Wallet::ACL::LDAP::Attribute checks whether the LDAP record for the
++entry corresponding to a principal contains an attribute with a
++particular value. It is used to verify ACL lines of type
++C<ldap-attr>. The value of such an ACL is a valid LDAP filter, and
++the ACL grants access to a given principal if and only if an LDAP
++search using a filter constructed of the principal filter AND
++the ACL filter returns a single entry.
+
+ To use this object, several configuration parameters must be set. See
+ L<Wallet::Config> for details on those configuration parameters and
+@@ -183,10 +185,9 @@
+ =item check(PRINCIPAL, ACL)
+
+ Returns true if PRINCIPAL is granted access according to ACL, false if
+-not, and undef on an error (see L<"DIAGNOSTICS"> below). ACL must be an
+-attribute name and a value, separated by an equal sign (with no
+-whitespace). PRINCIPAL will be granted access if its LDAP entry contains
+-that attribute with that value.
++not, and undef on an error (see L<"DIAGNOSTICS"> below). ACL must be
++a valid LDAP filter. The filter formed using the PRINCIPAL and the
++ACL filter must return a single entry for access to be granted.
+
+ =item error()
+
+@@ -216,31 +217,29 @@
+
+ =over 4
+
+-=item cannot check LDAP attribute %s for %s: %s
++=item search for %s failed in LDAP: %s
+
+-The LDAP compare to check for the required attribute failed. The
+-attribute may have been misspelled, or there may be LDAP directory
+-permission issues. This error indicates that PRINCIPAL's entry was
+-located in LDAP, but the check failed during the compare to verify the
+-attribute value.
++The search for an ldap entry failed because of a configuration error
++in Wallet or the LDAP server. For example the Wallet configuration
++includes an invalid root DN.
+
+-=item cannot search for %s in LDAP: %s
++=item malformed ldap-attr LDAP filter, no equal sign present
+
+-Searching for PRINCIPAL (possibly after ldap_map_principal() mapping)
+-failed. This is often due to LDAP directory permissions issues. This
+-indicates a failure during the mapping of PRINCIPAL to an LDAP DN.
++The ACL filter stored as ldap-attr is not a valid LDAP filter.
+
+-=item malformed ldap-attr ACL
++=item malformed ldap-attr LDAP filter, parenthesis mismatch
+
+-The ACL parameter to check() was malformed. Usually this means that
+-either the attribute or the value were empty or the required C<=> sign
+-separating them was missing.
++The ACL filter stored as ldap-attr is not a valid LDAP filter.
+
+ =item mapping principal to LDAP failed: %s
+
+ There was an ldap_map_principal() function defined in the wallet
+ configuration, but calling it for the PRINCIPAL argument failed.
+
++=item no ACL specified
++
++The ACL parameter to check() was undefined or the empty string.
++
+ =item no principal specified
+
+ The PRINCIPAL parameter to check() was undefined or the empty string.
diff --git a/debian/patches/series b/debian/patches/series
index 9c099b2..dfeb95f 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,25 @@
+0001-allow-quilt.patch
0001-webauth-retired.patch
+0002-password-charset.patch
0002-upstream-changes-was-modified.patch
+0003-checksum.patch
+0004-news.patch
+0005-checksum-doc.patch
+0006-file-crypt.patch
+0007-class-error.patch
+0008-invalid-ldap-url.patch
+0009-require-perl-gssapi.patch
+0010-empty-store.patch
+0011-doc.patch
+0012-pwd-options.patch
+0013-crypt-fixup.patch
+0014-crypt-custom.patch
+0015-encryption-fixes.patch
+0016-checkfile.patch
+0017-checkfile.patch
+0018-ad-length.patch
+0019-password-encrypt.patch
+0020-retire-webauth.patch
+0021-spx-test.patch
+0022-password-double-encryption.patch
+0023-ldap-attr-filter.patch