]> eyrie.org Git - kerberos/krb5-strength.git/commitdiff
Add new --check-only option to heimdal-history
authorRuss Allbery <eagle@eyrie.org>
Sun, 17 May 2020 02:24:53 +0000 (19:24 -0700)
committerRuss Allbery <eagle@eyrie.org>
Sun, 17 May 2020 02:29:50 +0000 (19:29 -0700)
Add new -c (--check-only) option to heimdal-history to check whether a
password would be accepted without updating the history or password
length databases.  Based on work by macrotex.

Closes #1

NEWS
tests/tools/heimdal-history-t
tools/heimdal-history

diff --git a/NEWS b/NEWS
index a57b84ff41f0cbfef8627965975ac2a18cf79ec2..7c3534ca8c903da30f62f5d41baa15a686fd7b72 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@
 
 krb5-strength 3.2 (unreleased)
 
+    Add new -c (--check-only) option to heimdal-history to check whether a
+    password would be accepted without updating the history or password
+    length databases.  Based on work by macrotex.
+
     Support building without CrackLib support by passing
     --without-cracklib to configure.  This makes the code a bit simpler
     and lighter if you don't intend to ever use the CrackLib support.
index b88f997085e5506a3bae9df41046096137347f29..f52467c10fc229c93d8ee5bc3b409d9332ea3276 100755 (executable)
@@ -39,11 +39,12 @@ use_prereq('Readonly');
 #
 # $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";
@@ -64,6 +65,7 @@ sub run_heimdal_history {
         '-S' => "$tmpdir/lengths.db",
         '-s' => $strength,
     );
+    push(@options, @extra);
 
     # Run the password strength checker.
     my ($out, $err);
@@ -135,7 +137,7 @@ my $tests = load_password_tests('history.json');
 
 # 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.
@@ -158,6 +160,19 @@ ok(tie(%lengthdb, 'DB_File::Lock', [$path, $mode, oct(600)], 'write'),
     '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();
index 54f55dc5d2f2d653ac290e962b3ec7c3310c2f16..ec85a9ba6ce3c7cc48ad9ed9229f951a327fdbf7 100755 (executable)
@@ -589,11 +589,12 @@ local $0 = basename($0);
 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) {
@@ -647,10 +648,12 @@ if (check_history($database, $principal, $password)) {
 # 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__
@@ -660,7 +663,7 @@ __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
@@ -671,7 +674,7 @@ heimdal-history - Password history via Heimdal external strength checking
 
 =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
@@ -714,6 +717,11 @@ unused here), and <password> is the new password.  There must be exactly one
 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
@@ -770,6 +778,13 @@ acceptable if it is within 0.005 seconds of the target time.  The results will
 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