3 # Test suite for basic Heimdal external strength checking functionality.
5 # Written by Russ Allbery <rra@stanford.edu>
6 # Copyright 2009, 2012, 2013
7 # The Board of Trustees of the Leland Stanford Junior University
9 # See LICENSE for licensing terms.
15 use lib "$ENV{SOURCE}/tap/perl";
17 use File::Copy qw(copy);
18 use Test::RRA qw(use_prereq);
19 use Test::RRA::Automake qw(test_file_path);
21 use_prereq('File::Slurp');
22 use_prereq('IPC::Run', 'run');
24 use_prereq('Test::More', '0.87_01');
26 # Run the newly-built heimdal-strength command to check a password and reports
27 # the results using Test::More. This uses the standard protocol for Heimdal
28 # external password strength checking programs.
30 # $test_ref - Reference to hash of test parameters
31 # name - The name of the test case
32 # principal - The principal changing its password
33 # password - The new password
34 # status - If present, the exit status (otherwise, it should be 0)
35 # error - If present, the expected rejection error
38 # Throws: Text exception on failure to run the test program
42 # Build the input to the strength checking program.
43 my $in = "principal: $test_ref->{principal}\n";
44 $in .= "new-password: $test_ref->{password}\n";
47 # Find the newly-built password checking program.
48 my $program = test_file_path('../external/heimdal-strength');
50 # Run the password strength checker.
53 run([$program, $test_ref->{principal}], \$in, \$out, \$err);
54 my $status = ($? >> 8);
57 # Check the results. If there is an error in the password, it should come
58 # on standard error; otherwise, standard output should be APPROVED. If
59 # there is a non-zero exit status, we expect the error on standard error
60 # and use that field to check for system errors.
61 is($status, $test_ref->{status} || 0, "$test_ref->{name} (status)");
62 if (defined($test_ref->{error})) {
63 is($err, $test_ref->{error}, '...error message');
64 is($out, q{}, '...no output');
66 is($err, q{}, '...no errors');
67 is($out, "APPROVED", '...approved');
72 # Create a new krb5.conf file that includes password dictionary configuration
73 # so that subsequent invocations of heimdal-strength can find the testing
76 # Returns: Path to the new krb5.conf file
77 # Throws: Text exception if the new krb5.conf file cannot be created
78 sub create_krb5_conf {
79 my $old = test_file_path('data/krb5.conf');
80 my $tmpdir = $ENV{BUILD} ? "$ENV{BUILD}/tmp" : 'tests/tmp';
81 my $new = "$tmpdir/krb5.conf";
83 # Create a temporary directory for the new file.
85 mkdir($tmpdir, 0777) or die "Cannot create $tmpdir: $!\n";
88 # Start with the testing krb5.conf file shipped in the package.
89 copy($old, $new) or die "Cannot copy $old to $new: $!\n";
91 # Append the local configuration.
92 my $datadir = $ENV{BUILD} ? "$ENV{BUILD}/data" : 'tests/data';
93 open(my $config, '>>', $new) or die "Cannot append to $new: $!\n";
94 print {$config} <<"__END__"
98 password_dictionary = $datadir/dictionary
101 or die "Cannot append to $new: $!\n";
102 close($config) or die "Cannot append to $new: $!\n";
104 # Return the path to the new file.
108 # Load a password test cases and return them as a list.
110 # $file - The path to the file containing the test data in JSON
112 # Returns: List of anonymous hashes representing password test cases
113 # Throws: Text exception on failure to load the test data
114 sub load_password_tests {
117 # Load the test file data into memory.
118 my $testdata = read_file($file);
120 # Decode the JSON into Perl objects and return them.
121 my $json = JSON->new->utf8;
122 return $json->decode($testdata);
125 # Load the password tests from JSON.
126 my $tests = load_password_tests(test_file_path('data/cracklib.json'));
128 # We can now calculate our plan: three tests for each password test, with an
129 # extra set for testing behavior when password_dictionary is not configured.
130 plan(tests => (scalar(@{$tests}) + 1) * 3);
132 # Find our initial test krb5.conf file.
133 local $ENV{KRB5_CONFIG} = test_file_path('data/krb5.conf');
135 # Run a test with no Kerberos password dictionary configuration and check that
136 # we get the correct error message.
137 my $error = 'Cannot initialize strength checking: password_dictionary not'
138 . ' configured in krb5.conf';
140 name => 'no dictionary configured',
141 principal => 'test@EXAMPLE.COM',
142 password => 'password',
146 check_password($test);
148 # Install the krb5.conf file with a configuration pointing to the test
150 local $ENV{KRB5_CONFIG} = create_krb5_conf();
152 # Run the password tests from JSON.
153 for my $test (@{$tests}) {
154 check_password($test);
157 # Clean up our temporary krb5.conf file on any exit.
159 my $tmpdir = $ENV{BUILD} ? "$ENV{BUILD}/tmp" : 'tests/tmp';
160 my $config = "$tmpdir/krb5.conf";
162 unlink($config) or warn "Cannot remove $config\n";