]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/heimdal/external-t
Use the Kerberos context to set the error message
[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 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.
29 #
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
36 #
37 # Returns: undef
38 #  Throws: Text exception on failure to run the test program
39 sub check_password {
40     my ($test_ref) = @_;
41
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";
45     $in .= "end\n";
46
47     # Find the newly-built password checking program.
48     my $program = test_file_path('../external/heimdal-strength');
49
50     # Run the password strength checker.
51     my $out;
52     my $err;
53     run([$program, $test_ref->{principal}], \$in, \$out, \$err);
54     my $status = ($? >> 8);
55     chomp($out, $err);
56
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');
65     } else {
66         is($err, q{},        '...no errors');
67         is($out, "APPROVED", '...approved');
68     }
69     return;
70 }
71
72 # Create a new krb5.conf file that includes password dictionary configuration
73 # so that subsequent invocations of heimdal-strength can find the testing
74 # dictionary.
75 #
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";
82
83     # Create a temporary directory for the new file.
84     if (!-d $tmpdir) {
85         mkdir($tmpdir, 0777) or die "Cannot create $tmpdir: $!\n";
86     }
87
88     # Start with the testing krb5.conf file shipped in the package.
89     copy($old, $new) or die "Cannot copy $old to $new: $!\n";
90
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__"
95
96 [appdefaults]
97     krb5-strength = {
98         password_dictionary = $datadir/dictionary
99     }
100 __END__
101       or die "Cannot append to $new: $!\n";
102     close($config) or die "Cannot append to $new: $!\n";
103
104     # Return the path to the new file.
105     return $new;
106 }
107
108 # Load a password test cases and return them as a list.
109 #
110 # $file - The path to the file containing the test data in JSON
111 #
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 {
115     my ($file) = @_;
116
117     # Load the test file data into memory.
118     my $testdata = read_file($file);
119
120     # Decode the JSON into Perl objects and return them.
121     my $json = JSON->new->utf8;
122     return $json->decode($testdata);
123 }
124
125 # Load the password tests from JSON.
126 my $tests = load_password_tests(test_file_path('data/cracklib.json'));
127
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);
131
132 # Find our initial test krb5.conf file.
133 local $ENV{KRB5_CONFIG} = test_file_path('data/krb5.conf');
134
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';
139 my $test = {
140     name      => 'no dictionary configured',
141     principal => 'test@EXAMPLE.COM',
142     password  => 'password',
143     status    => 1,
144     error     => $error,
145 };
146 check_password($test);
147
148 # Install the krb5.conf file with a configuration pointing to the test
149 # dictionary.
150 local $ENV{KRB5_CONFIG} = create_krb5_conf();
151
152 # Run the password tests from JSON.
153 for my $test (@{$tests}) {
154     check_password($test);
155 }
156
157 # Clean up our temporary krb5.conf file on any exit.
158 END {
159     my $tmpdir = $ENV{BUILD} ? "$ENV{BUILD}/tmp" : 'tests/tmp';
160     my $config = "$tmpdir/krb5.conf";
161     if (-f $config) {
162         unlink($config) or warn "Cannot remove $config\n";
163         rmdir($tmpdir);
164     }
165 }