diff options
author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-05-15 11:43:01 -0300 |
---|---|---|
committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-05-15 11:43:01 -0300 |
commit | 6e039fc6475d1dec21f7aa280c13f1c0b071f56c (patch) | |
tree | 22ced3820ff2b0dc302856c7fcd62f079c7708df | |
parent | 677b3a938f2f714109b47880fdf364183fdb61b2 (diff) |
attached patches from Bill MacAllister
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 |