]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/plugin/heimdal-t.c
bd5c2596a644f08bb5f0384b7ed51ef4b17dd5f8
[kerberos/krb5-strength.git] / tests / plugin / heimdal-t.c
1 /*
2  * Test for the Heimdal shared module API.
3  *
4  * Written by Russ Allbery <eagle@eyrie.org>
5  * Copyright 2009, 2013
6  *     The Board of Trustees of the Leland Stanford Junior University
7  *
8  * See LICENSE for licensing terms.
9  */
10
11 #include <config.h>
12 #include <portable/krb5.h>
13 #include <portable/system.h>
14
15 #include <dlfcn.h>
16 #include <errno.h>
17 #ifdef HAVE_KADM5_KADM5_PWCHECK_H
18 # include <kadm5/kadm5-pwcheck.h>
19 #endif
20
21 #include <tests/tap/basic.h>
22 #include <tests/tap/kerberos.h>
23 #include <tests/tap/process.h>
24 #include <tests/tap/string.h>
25 #include <util/macros.h>
26
27 /*
28  * The password test data, generated from the JSON source.  Defines an arrays
29  * named cdb_tests, cracklib_tests, and principal_tests.
30  */
31 #include <tests/data/passwords/cdb.c>
32 #include <tests/data/passwords/classes.c>
33 #include <tests/data/passwords/cracklib.c>
34 #include <tests/data/passwords/length.c>
35 #include <tests/data/passwords/letter.c>
36 #include <tests/data/passwords/principal.c>
37
38
39 #ifndef HAVE_KADM5_KADM5_PWCHECK_H
40 /*
41  * If we're not building with Heimdal, we can't run this test and much of the
42  * test won't even compile.  Replace this test with a small program that just
43  * calls skip_all.
44  */
45 int
46 main(void)
47 {
48     skip_all("not built against Heimdal libraries");
49     return 0;
50 }
51
52 #else
53
54 /*
55  * Loads the Heimdal password change plugin and tests that its metadata is
56  * correct.  Returns a pointer to the kadm5_pw_policy_verifier struct or bails
57  * on failure to load the plugin.  Stores the handle in the last argument so
58  * that the caller can free the handle at the end of the test suite.
59  */
60 static struct kadm5_pw_policy_verifier *
61 load_plugin(void **handle)
62 {
63     char *path;
64     struct kadm5_pw_policy_verifier *verifier;
65
66     /* Load the module. */
67     path = test_file_path("../plugin/.libs/strength.so");
68     if (path == NULL)
69         bail("cannot find plugin");
70     *handle = dlopen(path, RTLD_NOW);
71     if (*handle == NULL)
72         bail("cannot dlopen %s: %s", path, dlerror());
73     test_file_path_free(path);
74
75     /* Find the dispatch table and do a basic sanity check. */
76     verifier = dlsym(*handle, "kadm5_password_verifier");
77     if (verifier == NULL)
78         bail("cannot get kadm5_password_verifier symbol: %s", dlerror());
79     if (verifier->funcs == NULL || verifier->funcs[0].func == NULL)
80         bail("no verifier functions in module");
81
82     /* Verify the metadata. */
83     is_string("krb5-strength", verifier->name, "Module name");
84     is_string("Russ Allbery", verifier->vendor, "Module vendor");
85     is_int(KADM5_PASSWD_VERSION_V1, verifier->version, "Module version");
86     is_string("krb5-strength", verifier->funcs[0].name,
87               "Module function name");
88     ok(verifier->funcs[1].name == NULL, "Only one function in module");
89
90     /* Return the dispatch table. */
91     return verifier;
92 }
93
94
95 /*
96  * Given the dispatch table and a test case, call out to the password strength
97  * checking module and check the results.
98  */
99 static void
100 is_password_test(const struct kadm5_pw_policy_verifier *verifier,
101                  const struct password_test *test)
102 {
103     krb5_context ctx;
104     krb5_principal princ;
105     krb5_error_code code;
106     krb5_data password;
107     int result;
108     char error[BUFSIZ] = "";
109
110     /* Obtain a Kerberos context to use for parsing principal names. */
111     code = krb5_init_context(&ctx);
112     if (code != 0)
113         bail_krb5(ctx, code, "cannot initialize Kerberos context");
114
115     /* Translate the test data into the form that the verifier expects. */
116     code = krb5_parse_name(ctx, test->principal, &princ);
117     if (code != 0)
118         bail_krb5(ctx, code, "cannot parse principal %s", test->principal);
119     password.data = (char *) test->password;
120     password.length = strlen(test->password);
121
122     /* Call the verifier. */
123     result = (verifier->funcs[0].func)(ctx, princ, &password, NULL, error,
124                                        sizeof(error));
125
126     /* Heimdal only returns 0 or 1, so translate the expected code. */
127     is_int(test->code == 0 ? 0 : 1, result, "%s (status)", test->name);
128     is_string(test->error == NULL ? "" : test->error, error, "%s (error)",
129               test->name);
130
131     /* Free data structures. */
132     krb5_free_principal(ctx, princ);
133     krb5_free_context(ctx);
134 }
135
136
137 int
138 main(void)
139 {
140     char *path, *krb5_config, *krb5_config_empty, *tmpdir;
141     char *setup_argv[10];
142     size_t i, count;
143     struct kadm5_pw_policy_verifier *verifier;
144     void *handle;
145
146     /*
147      * Calculate how many tests we have.  There are five tests for the module
148      * metadata and two tests per password test.  We run the principal tests
149      * twice, once with CrackLib and once with CDB.
150      */
151     count = ARRAY_SIZE(cracklib_tests);
152     count += ARRAY_SIZE(cdb_tests);
153     count += ARRAY_SIZE(classes_tests);
154     count += ARRAY_SIZE(length_tests);
155     count += ARRAY_SIZE(letter_tests);
156     count += ARRAY_SIZE(principal_tests) * 2;
157     plan(5 + count * 2);
158
159     /* Start with the krb5.conf that contains no dictionary configuration. */
160     path = test_file_path("data/krb5.conf");
161     if (path == NULL)
162         bail("cannot find data/krb5.conf in the test suite");
163     basprintf(&krb5_config_empty, "KRB5_CONFIG=%s", path);
164     putenv(krb5_config_empty);
165
166     /* Load the plugin. */
167     verifier = load_plugin(&handle);
168
169     /* Set up our krb5.conf with the dictionary configuration. */
170     setup_argv[0] = test_file_path("data/make-krb5-conf");
171     if (setup_argv[0] == NULL)
172         bail("cannot find data/make-krb5-conf in the test suite");
173     tmpdir = test_tmpdir();
174     setup_argv[1] = path;
175     setup_argv[2] = tmpdir;
176     setup_argv[3] = (char *) "password_dictionary";
177     basprintf(&setup_argv[4], "%s/data/dictionary", getenv("BUILD"));
178     setup_argv[5] = NULL;
179     run_setup((const char **) setup_argv);
180
181     /* Point KRB5_CONFIG at the newly-generated krb5.conf file. */
182     basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir);
183     putenv(krb5_config);
184     free(krb5_config_empty);
185
186     /* Now, run all of the tests. */
187     for (i = 0; i < ARRAY_SIZE(cracklib_tests); i++)
188         is_password_test(verifier, &cracklib_tests[i]);
189     for (i = 0; i < ARRAY_SIZE(principal_tests); i++)
190         is_password_test(verifier, &principal_tests[i]);
191
192     /* Add simple character class restrictions. */
193     setup_argv[5] = (char *) "require_ascii_printable";
194     setup_argv[6] = (char *) "true";
195     setup_argv[7] = (char *) "require_non_letter";
196     setup_argv[8] = (char *) "true";
197     setup_argv[9] = NULL;
198     run_setup((const char **) setup_argv);
199
200     /* Run the simple character class tests. */
201     for (i = 0; i < ARRAY_SIZE(letter_tests); i++)
202         is_password_test(verifier, &letter_tests[i]);
203
204     /* Add complex character class restrictions and remove the dictionary. */
205     free(setup_argv[4]);
206     setup_argv[3] = (char *) "require_classes";
207     setup_argv[4] = (char *) "8-19:lower,upper 8-15:digit 8-11:symbol";
208     setup_argv[5] = NULL;
209     run_setup((const char **) setup_argv);
210
211     /* Run the simple character class tests. */
212     for (i = 0; i < ARRAY_SIZE(classes_tests); i++)
213         is_password_test(verifier, &classes_tests[i]);
214
215     /*
216      * Add length restrictions.  This should only do length checks without any
217      * dictionary checks.
218      */
219     setup_argv[3] = (char *) "minimum_length";
220     setup_argv[4] = (char *) "12";
221     setup_argv[5] = NULL;
222     run_setup((const char **) setup_argv);
223
224     /* Run the length tests. */
225     for (i = 0; i < ARRAY_SIZE(length_tests); i++)
226         is_password_test(verifier, &length_tests[i]);
227
228 #ifdef HAVE_CDB
229
230     /* If built with CDB, set up krb5.conf to use a CDB dictionary instead. */
231     setup_argv[3] = (char *) "password_dictionary_cdb";
232     setup_argv[4] = test_file_path("data/wordlist.cdb");
233     if (setup_argv[4] == NULL)
234         bail("cannot find data/wordlist.cdb in the test suite");
235     setup_argv[5] = NULL;
236     run_setup((const char **) setup_argv);
237     test_file_path_free(setup_argv[0]);
238     test_file_path_free(setup_argv[4]);
239     test_file_path_free(path);
240
241     /* Run the CDB tests. */
242     for (i = 0; i < ARRAY_SIZE(cdb_tests); i++)
243         is_password_test(verifier, &cdb_tests[i]);
244     for (i = 0; i < ARRAY_SIZE(principal_tests); i++)
245         is_password_test(verifier, &principal_tests[i]);
246
247 #else /* !HAVE_CDB */
248
249     /* Otherwise, mark the CDB tests as skipped. */
250     count = ARRAY_SIZE(cdb_tests) + ARRAY_SIZE(principal_tests);
251     skip_block(count * 2, "not built with CDB support");
252
253 #endif /* !HAVE_CDB */
254
255     /* Manually clean up after the results of make-krb5-conf. */
256     basprintf(&path, "%s/krb5.conf", tmpdir);
257     unlink(path);
258     free(path);
259     test_tmpdir_free(tmpdir);
260
261     /* Close down the module. */
262     if (dlclose(handle) != 0)
263         bail("cannot close plugin: %s", dlerror());
264
265     /* Keep valgrind clean by freeing environmental memory. */
266     putenv((char *) "KRB5_CONFIG=");
267     free(krb5_config);
268     return 0;
269 }
270
271 #endif /* HAVE_KADM5_KADM5_PWCHECK_H */