#
# $principal - Principal to pass to the command
# $password - Password to pass to the command
+# @extra - Additional options to pass to heimdal-history
#
# Returns: The exit status, standard output, and standard error as a list
# Throws: Text exception on failure to run the test program
sub run_heimdal_history {
- my ($principal, $password) = @_;
+ my ($principal, $password, @extra) = @_;
# Build the input to the strength checking program.
my $in = "principal: $principal\n";
'-S' => "$tmpdir/lengths.db",
'-s' => $strength,
);
+ push(@options, @extra);
# Run the password strength checker.
my ($out, $err);
# Calculate and declare the plan. We run three tests for each password test,
# and then do some additional testing of the length statistics.
-plan(tests => scalar(@{$tests}) * 3 + 2);
+plan(tests => scalar(@{$tests}) * 3 + 8);
# Point to a generic krb5.conf file. This ensures that the heimdal-strength
# program will only do principal-based strength checks.
'Length database exists');
is_deeply(\%lengthdb, \%lengths, '...and contents are correct');
+# Check the same password twice in a row with the -c option. It should be
+# accepted both times, instead of rejected the second time as a duplicate.
+my ($status, $out, $err)
+ = run_heimdal_history('test@EXAMPLE.ORG', 'somepass', '-c');
+is($status, 0, 'First password check succeeds');
+is($out, "APPROVED\n", '...with correct output');
+is($err, q{}, '...and no error');
+($status, $out, $err)
+ = run_heimdal_history('test@EXAMPLE.ORG', 'somepass', '-c');
+is($status, 0, 'Second password check still succeeds');
+is($out, "APPROVED\n", '...with correct output');
+is($err, q{}, '...and no error');
+
# Clean up the databases and lock files on any exit.
END {
my $tmpdir = test_tmpdir();
my ($opt, $usage) = describe_options(
'%c %o',
['benchmark|b=f', 'Benchmark hash iterations for this target time'],
+ ['check-only|c', 'Check password history without updating database'],
['database|d=s', 'Path to the history database, overriding the default'],
['help|h', 'Print usage message and exit'],
['manual|man|m', 'Print full manual and exit'],
['quiet|q', 'Suppress logging to syslog'],
- ['stats|S=s', 'Path to hash of length statistics'],
+ ['stats|S=s', 'Path to database of length statistics'],
['strength|s=s', 'Path to strength checking program to run'],
);
if ($opt->help) {
# The password is accepted. Record it, update the length counter, and return
# success.
log_result($principal, 'accepted');
-write_history($database, $principal, $password);
+if (!$opt->check_only) {
+ write_history($database, $principal, $password);
+ update_length_counts($stats_db, length($password));
+}
say {*STDOUT} 'APPROVED'
or die "$0: cannot write to standard output: $!\n";
-update_length_counts($stats_db, length($password));
exit(0);
__END__
##############################################################################
=for stopwords
-heimdal-history heimdal-strength Heimdal -hmq BerkeleyDB timestamps POSIX
+heimdal-history heimdal-strength Heimdal -chmq BerkeleyDB timestamps POSIX
whitespace API Allbery sublicense MERCHANTABILITY NONINFRINGEMENT syslog
pseudorandom JSON LDAP-compatible PBKDF2 SHA-256 KDC SPDX-License-Identifier
MIT
=head1 SYNOPSIS
-B<heimdal-history> [B<-hmq>] [B<-b> I<target-time>] [B<-d> I<database>]
+B<heimdal-history> [B<-chmq>] [B<-b> I<target-time>] [B<-d> I<database>]
[B<-S> I<length-stats-db>] [B<-s> I<strength-program>] [B<principal>]
=head1 DESCRIPTION
space after the colon. Any subsequent spaces are taken to be part of the
principal or password.
+If the password is accepted, B<heimdal-history> will assume that it will be
+used and will update the history database to record the new password. It will
+also update the password length statistics database to account for the new
+password.
+
If invoked as root, B<heimdal-history> will run the external strength checking
program as user C<nobody> and group C<nogroup>, and will check and write to
the history database as user C<_history> and group C<_history>. These users
be printed to standard output and then B<heimdal-history> will exit
successfully.
+=item B<-c>, B<--check-only>
+
+Check password history and password strength and print the results as normal,
+but do not update the history or length statistics databases. This is a
+read-only mode of operation that will not make any changes to the underlying
+database, only report if a password would currently be accepted.
+
=item B<-d> I<database>, B<--database>=I<database>
Use I<database> as the history database file instead of the default