2 * Test for the Heimdal shared module API.
4 * Written by Russ Allbery <eagle@eyrie.org>
6 * The Board of Trustees of the Leland Stanford Junior University
8 * See LICENSE for licensing terms.
12 #include <portable/krb5.h>
13 #include <portable/system.h>
17 #ifdef HAVE_KADM5_KADM5_PWCHECK_H
18 # include <kadm5/kadm5-pwcheck.h>
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>
28 * The password test data, generated from the JSON source. Defines an arrays
29 * named cdb_tests, cracklib_tests, and principal_tests.
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>
39 #ifndef HAVE_KADM5_KADM5_PWCHECK_H
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
48 skip_all("not built against Heimdal libraries");
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.
60 static struct kadm5_pw_policy_verifier *
61 load_plugin(void **handle)
64 struct kadm5_pw_policy_verifier *verifier;
66 /* Load the module. */
67 path = test_file_path("../plugin/.libs/strength.so");
69 bail("cannot find plugin");
70 *handle = dlopen(path, RTLD_NOW);
72 bail("cannot dlopen %s: %s", path, dlerror());
73 test_file_path_free(path);
75 /* Find the dispatch table and do a basic sanity check. */
76 verifier = dlsym(*handle, "kadm5_password_verifier");
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");
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");
90 /* Return the dispatch table. */
96 * Given the dispatch table and a test case, call out to the password strength
97 * checking module and check the results.
100 is_password_test(const struct kadm5_pw_policy_verifier *verifier,
101 const struct password_test *test)
104 krb5_principal princ;
105 krb5_error_code code;
108 char error[BUFSIZ] = "";
110 /* Obtain a Kerberos context to use for parsing principal names. */
111 code = krb5_init_context(&ctx);
113 bail_krb5(ctx, code, "cannot initialize Kerberos context");
115 /* Translate the test data into the form that the verifier expects. */
116 code = krb5_parse_name(ctx, test->principal, &princ);
118 bail_krb5(ctx, code, "cannot parse principal %s", test->principal);
119 password.data = (char *) test->password;
120 password.length = strlen(test->password);
122 /* Call the verifier. */
123 result = (verifier->funcs[0].func)(ctx, princ, &password, NULL, error,
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)",
131 /* Free data structures. */
132 krb5_free_principal(ctx, princ);
133 krb5_free_context(ctx);
140 char *path, *krb5_config, *krb5_config_empty, *tmpdir;
141 char *setup_argv[10];
143 struct kadm5_pw_policy_verifier *verifier;
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.
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;
159 /* Start with the krb5.conf that contains no dictionary configuration. */
160 path = test_file_path("data/krb5.conf");
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);
166 /* Load the plugin. */
167 verifier = load_plugin(&handle);
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);
181 /* Point KRB5_CONFIG at the newly-generated krb5.conf file. */
182 basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir);
184 free(krb5_config_empty);
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]);
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);
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]);
204 /* Add complex character class restrictions and remove the dictionary. */
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);
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]);
216 * Add length restrictions. This should only do length checks without any
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);
224 /* Run the length tests. */
225 for (i = 0; i < ARRAY_SIZE(length_tests); i++)
226 is_password_test(verifier, &length_tests[i]);
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);
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]);
247 #else /* !HAVE_CDB */
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");
253 #endif /* !HAVE_CDB */
255 /* Manually clean up after the results of make-krb5-conf. */
256 basprintf(&path, "%s/krb5.conf", tmpdir);
259 test_tmpdir_free(tmpdir);
261 /* Close down the module. */
262 if (dlclose(handle) != 0)
263 bail("cannot close plugin: %s", dlerror());
265 /* Keep valgrind clean by freeing environmental memory. */
266 putenv((char *) "KRB5_CONFIG=");
271 #endif /* HAVE_KADM5_KADM5_PWCHECK_H */