]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/heimdal/external-t
Move heimdal-strength to the tools directory
[kerberos/krb5-strength.git] / tests / heimdal / external-t
1 #!/usr/bin/perl
2 #
3 # Test suite for basic Heimdal external strength checking functionality.
4 #
5 # Written by Russ Allbery <rra@stanford.edu>
6 # Copyright 2009, 2012, 2013
7 #     The Board of Trustees of the Leland Stanford Junior University
8 #
9 # See LICENSE for licensing terms.
10
11 use 5.006;
12 use strict;
13 use warnings;
14
15 use lib "$ENV{SOURCE}/tap/perl";
16
17 use File::Copy qw(copy);
18 use Test::RRA qw(use_prereq);
19 use Test::RRA::Automake qw(test_file_path);
20
21 use_prereq('File::Slurp');
22 use_prereq('IPC::Run', 'run');
23 use_prereq('JSON');
24 use_prereq('Test::More', '0.87_01');
25
26 # Run the newly-built heimdal-strength command and return the status, output,
27 # and error output as a list.
28 #
29 # $principal - Principal to pass to the command
30 # $password  - Password to pass to the command
31 #
32 # Returns: The exit status, standard output, and standard error as a list
33 #  Throws: Text exception on failure to run the test program
34 sub run_heimdal_strength {
35     my ($principal, $password) = @_;
36
37     # Build the input to the strength checking program.
38     my $in = "principal: $principal\n";
39     $in .= "new-password: $password\n";
40     $in .= "end\n";
41
42     # Find the newly-built password checking program.
43     my $program = test_file_path('../tools/heimdal-strength');
44
45     # Run the password strength checker.
46     my $out;
47     my $err;
48     run([$program, $principal], \$in, \$out, \$err);
49     my $status = ($? >> 8);
50
51     # Return the results.
52     return ($status, $out, $err);
53 }
54
55 # Run the newly-built heimdal-strength command to check a password and reports
56 # the results using Test::More.  This uses the standard protocol for Heimdal
57 # external password strength checking programs.
58 #
59 # $test_ref - Reference to hash of test parameters
60 #   name      - The name of the test case
61 #   principal - The principal changing its password
62 #   password  - The new password
63 #   status    - If present, the exit status (otherwise, it should be 0)
64 #   error     - If present, the expected rejection error
65 #
66 # Returns: undef
67 #  Throws: Text exception on failure to run the test program
68 sub check_password {
69     my ($test_ref) = @_;
70     my $principal = $test_ref->{principal};
71     my $password  = $test_ref->{password};
72
73     # Run the heimdal-strength command.
74     my ($status, $out, $err) = run_heimdal_strength($principal, $password);
75     chomp($out, $err);
76
77     # Check the results.  If there is an error in the password, it should come
78     # on standard error; otherwise, standard output should be APPROVED.  If
79     # there is a non-zero exit status, we expect the error on standard error
80     # and use that field to check for system errors.
81     is($status, $test_ref->{status} || 0, "$test_ref->{name} (status)");
82     if (defined($test_ref->{error})) {
83         is($err, $test_ref->{error}, '...error message');
84         is($out, q{}, '...no output');
85     } else {
86         is($err, q{},        '...no errors');
87         is($out, "APPROVED", '...approved');
88     }
89     return;
90 }
91
92 # Create a new krb5.conf file that includes password dictionary configuration
93 # so that subsequent invocations of heimdal-strength can find the testing
94 # dictionary.  Takes the setting and the value of the setting to write.
95 #
96 # $setting    - krb5.conf setting to add to [appdefaults]
97 # $dictionary - The dictionary file name, relative to tests/data
98 #
99 # Returns: Path to the new krb5.conf file
100 #  Throws: Text exception if the new krb5.conf file cannot be created
101 sub create_krb5_conf {
102     my ($setting, $dictionary) = @_;
103     my $old    = test_file_path('data/krb5.conf');
104     my $tmpdir = $ENV{BUILD} ? "$ENV{BUILD}/tmp" : 'tests/tmp';
105     my $new    = "$tmpdir/krb5.conf";
106
107     # Create a temporary directory for the new file.
108     if (!-d $tmpdir) {
109         mkdir($tmpdir, 0777) or die "Cannot create $tmpdir: $!\n";
110     }
111
112     # Start with the testing krb5.conf file shipped in the package.
113     copy($old, $new) or die "Cannot copy $old to $new: $!\n";
114
115     # Append the local configuration.
116     my $datadir = $ENV{BUILD} ? "$ENV{BUILD}/data" : 'tests/data';
117     open(my $config, '>>', $new) or die "Cannot append to $new: $!\n";
118     print {$config} <<"__END__"
119
120 [appdefaults]
121     krb5-strength = {
122         $setting = $datadir/$dictionary
123     }
124 __END__
125       or die "Cannot append to $new: $!\n";
126     close($config) or die "Cannot append to $new: $!\n";
127
128     # Return the path to the new file.
129     return $new;
130 }
131
132 # Load a password test cases and return them as a list.
133 #
134 # $file - The path to the file containing the test data in JSON
135 #
136 # Returns: List of anonymous hashes representing password test cases
137 #  Throws: Text exception on failure to load the test data
138 sub load_password_tests {
139     my ($file) = @_;
140
141     # Load the test file data into memory.
142     my $testdata = read_file($file);
143
144     # Decode the JSON into Perl objects and return them.
145     my $json = JSON->new->utf8;
146     return $json->decode($testdata);
147 }
148
149 # Load the password tests from JSON.
150 my $cracklib_tests = load_password_tests(test_file_path('data/cracklib.json'));
151 my $cdb_tests      = load_password_tests(test_file_path('data/cdb.json'));
152
153 # We can now calculate our plan: three tests for each password test, with an
154 # extra set for testing behavior when password_dictionary is not configured.
155 plan(tests => (scalar(@{$cracklib_tests}) + scalar(@{$cdb_tests}) + 1) * 3);
156
157 # Find our initial test krb5.conf file.
158 local $ENV{KRB5_CONFIG} = test_file_path('data/krb5.conf');
159
160 # Run a test with no Kerberos password dictionary configuration and check that
161 # we get the correct error message.
162 my $error = 'Cannot initialize strength checking: password_dictionary not'
163   . ' configured in krb5.conf';
164 my $test = {
165     name      => 'no dictionary configured',
166     principal => 'test@EXAMPLE.COM',
167     password  => 'password',
168     status    => 1,
169     error     => $error,
170 };
171 check_password($test);
172
173 # Install the krb5.conf file with a configuration pointing to the test
174 # CrackLib dictionary.
175 my $krb5_conf = create_krb5_conf('password_dictionary', 'dictionary');
176 local $ENV{KRB5_CONFIG} = $krb5_conf;
177
178 # Run the CrackLib password tests from JSON.
179 for my $test (@{$cracklib_tests}) {
180     check_password($test);
181 }
182
183 # Install the krb5.conf file with configuration pointing to the CDB
184 # dictionary.
185 $krb5_conf = create_krb5_conf('password_dictionary_cdb', 'wordlist.cdb');
186 local $ENV{KRB5_CONFIG} = $krb5_conf;
187
188 # Check whether we were built with CDB support.  If so, run those tests.
189 my ($status, $output, $err) = run_heimdal_strength('test', 'password');
190 SKIP: {
191     skip('not built with CDB support', scalar(@{$cdb_tests}) * 3)
192       if ($status == 1 && $err =~ m{ not [ ] built [ ] with [ ] CDB }xms);
193
194     # Run the CDB password tests from JSON.
195     for my $test (@{$cdb_tests}) {
196         check_password($test);
197     }
198 }
199
200 # Clean up our temporary krb5.conf file on any exit.
201 END {
202     my $tmpdir = $ENV{BUILD} ? "$ENV{BUILD}/tmp" : 'tests/tmp';
203     my $config = "$tmpdir/krb5.conf";
204     if (-f $config) {
205         unlink($config) or warn "Cannot remove $config\n";
206         rmdir($tmpdir);
207     }
208 }