]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/plugin/mit-t.c
Reorganize the plugin tests
[kerberos/krb5-strength.git] / tests / plugin / mit-t.c
1 /*
2  * Test for the MIT Kerberos shared module API.
3  *
4  * Written by Russ Allbery <rra@stanford.edu>
5  * Copyright 2010, 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/kadmin.h>
13 #include <portable/krb5.h>
14 #include <portable/system.h>
15
16 #include <dlfcn.h>
17 #include <errno.h>
18 #ifdef HAVE_KRB5_PWQUAL_PLUGIN_H
19 # include <krb5/pwqual_plugin.h>
20 #endif
21
22 #include <tests/tap/basic.h>
23 #include <tests/tap/kerberos.h>
24 #include <tests/tap/process.h>
25 #include <tests/tap/string.h>
26 #include <util/macros.h>
27
28 /*
29  * The password test data, generated from the JSON source.  Defines an arrays
30  * named cdb_tests, cracklib_tests, and generic_tests.
31  */
32 #include <tests/data/passwords/cdb.c>
33 #include <tests/data/passwords/class.c>
34 #include <tests/data/passwords/cracklib.c>
35 #include <tests/data/passwords/generic.c>
36 #include <tests/data/passwords/length.c>
37
38
39 #ifndef HAVE_KRB5_PWQUAL_PLUGIN_H
40 /*
41  * If we're not building with MIT Kerberos, we can't run this test and much of
42  * the test won't even compile.  Replace this test with a small program that
43  * just calls skip_all.
44  */
45 int
46 main(void)
47 {
48     skip_all("not built against MIT libraries");
49     return 0;
50 }
51
52 #else
53
54 /* The public symbol that we load and call to get the vtable. */
55 typedef krb5_error_code pwqual_strength_initvt(krb5_context, int, int,
56                                                krb5_plugin_vtable);
57
58
59 /*
60  * Loads the Heimdal password change plugin and tests that its metadata is
61  * correct.  Returns a pointer to the kadm5_pw_policy_verifier struct or bails
62  * on failure to load the plugin.
63  */
64 static krb5_pwqual_vtable
65 load_plugin(krb5_context ctx)
66 {
67     char *path;
68     void *handle;
69     krb5_error_code code;
70     krb5_pwqual_vtable vtable = NULL;
71     krb5_error_code (*init)(krb5_context, int, int, krb5_plugin_vtable);
72
73     /* Load the module. */
74     path = test_file_path("../plugin/.libs/passwd_strength.so");
75     if (path == NULL)
76         bail("cannot find plugin");
77     handle = dlopen(path, RTLD_NOW);
78     if (handle == NULL)
79         bail("cannot dlopen %s: %s", path, dlerror());
80     test_file_path_free(path);
81
82     /* Find the entry point function. */
83     init = dlsym(handle, "pwqual_strength_initvt");
84     if (init == NULL)
85         bail("cannot get pwqual_strength_initvt symbol: %s", dlerror());
86
87     /* Test for correct results when requesting the wrong API version. */
88     code = init(ctx, 2, 0, (krb5_plugin_vtable) vtable);
89     is_int(code, KRB5_PLUGIN_VER_NOTSUPP,
90            "Correct status for bad major API version");
91
92     /* Call that function properly to get the vtable. */
93     vtable = bmalloc(sizeof(*vtable));
94     code = init(ctx, 1, 1, (krb5_plugin_vtable) vtable);
95     if (code != 0)
96         bail_krb5(ctx, code, "cannot obtain module vtable");
97
98     /* Check that all of the vtable entries are present. */
99     if (vtable->open == NULL || vtable->check == NULL || vtable->close == NULL)
100         bail("missing function in module vtable");
101
102     /* Verify the metadata. */
103     is_string("krb5-strength", vtable->name, "Module name");
104
105     /* Return the vtable. */
106     return vtable;
107 }
108
109
110 /*
111  * Given a Kerberos context, the dispatch table, the module data, and a test
112  * case, call out to the password strength checking module and check the
113  * results.
114  */
115 static void
116 is_password_test(krb5_context ctx, const krb5_pwqual_vtable vtable,
117                  krb5_pwqual_moddata data, const struct password_test *test)
118 {
119     krb5_principal princ;
120     krb5_error_code code;
121     const char *error;
122
123     /* Translate the principal into a krb5_principal. */
124     code = krb5_parse_name(ctx, test->principal, &princ);
125     if (code != 0)
126         bail_krb5(ctx, code, "cannot parse principal %s", test->principal);
127
128     /* Call the verifier. */
129     code = vtable->check(ctx, data, test->password, NULL, princ, NULL);
130
131     /* Check the results against the test data. */
132     is_int(test->code, code, "%s (status)", test->name);
133     if (code == 0)
134         is_string(test->error, NULL, "%s (error)", test->name);
135     else {
136         error = krb5_get_error_message(ctx, code);
137         is_string(test->error, error, "%s (error)", test->name);
138         krb5_free_error_message(ctx, error);
139     }
140
141     /* Free the parsed principal. */
142     krb5_free_principal(ctx, princ);
143 }
144
145
146 int
147 main(void)
148 {
149     char *path, *dictionary, *krb5_config, *krb5_config_empty, *tmpdir;
150     char *setup_argv[10];
151     const char*build;
152     size_t i, count;
153     krb5_context ctx;
154     krb5_pwqual_vtable vtable;
155     krb5_pwqual_moddata data;
156     krb5_error_code code;
157
158     /*
159      * Calculate how many tests we have.  There are two tests for the module
160      * metadata, six more tests for initializing the plugin, one test for
161      * initialization without a valid dictionary, and two tests per password
162      * test.
163      *
164      * We run all the CrackLib and generic tests twice, once with an explicit
165      * dictionary path and once from krb5.conf configuration.  We run the
166      * generic tests with both CrackLib and CDB configurations.
167      */
168     count = 2 * ARRAY_SIZE(cracklib_tests);
169     count += ARRAY_SIZE(cdb_tests);
170     count += ARRAY_SIZE(class_tests);
171     count += ARRAY_SIZE(length_tests);
172     count += 2 * ARRAY_SIZE(generic_tests);
173     plan(2 + 6 + count * 2);
174
175     /* Start with the krb5.conf that contains no dictionary configuration. */
176     path = test_file_path("data/krb5.conf");
177     if (path == NULL)
178         bail("cannot find data/krb5.conf in the test suite");
179     basprintf(&krb5_config_empty, "KRB5_CONFIG=%s", path);
180     putenv(krb5_config_empty);
181
182     /* Obtain a Kerberos context with that krb5.conf file. */
183     code = krb5_init_context(&ctx);
184     if (code != 0)
185         bail_krb5(ctx, code, "cannot initialize Kerberos context");
186
187     /* Load and initialize the plugin without a dictionary. */
188     vtable = load_plugin(ctx);
189     code = vtable->open(ctx, NULL, &data);
190     is_int(KADM5_MISSING_CONF_PARAMS, code,
191            "Plugin initialization (no dictionary)");
192
193     /* Initialize the plugin again with the correct dictionary. */
194     build = getenv("BUILD");
195     if (build == NULL)
196         bail("BUILD not set in the environment");
197     basprintf(&dictionary, "%s/data/dictionary", build);
198     code = vtable->open(ctx, dictionary, &data);
199     is_int(0, code, "Plugin initialization (explicit dictionary)");
200     if (code != 0)
201         bail("cannot continue after plugin initialization failure");
202
203     /* Now, run all of the tests, with generic tests. */
204     for (i = 0; i < ARRAY_SIZE(cracklib_tests); i++)
205         is_password_test(ctx, vtable, data, &cracklib_tests[i]);
206     for (i = 0; i < ARRAY_SIZE(generic_tests); i++)
207         is_password_test(ctx, vtable, data, &generic_tests[i]);
208
209     /* Close that initialization of the plugin and destroy that context. */
210     vtable->close(ctx, data);
211     krb5_free_context(ctx);
212     ctx = NULL;
213
214     /* Set up our krb5.conf with the dictionary configuration. */
215     tmpdir = test_tmpdir();
216     setup_argv[0] = test_file_path("data/make-krb5-conf");
217     if (setup_argv[0] == NULL)
218         bail("cannot find data/make-krb5-conf in the test suite");
219     setup_argv[1] = path;
220     setup_argv[2] = tmpdir;
221     setup_argv[3] = (char *) "password_dictionary";
222     setup_argv[4] = dictionary;
223     setup_argv[5] = NULL;
224     run_setup((const char **) setup_argv);
225
226     /* Point KRB5_CONFIG at the newly-generated krb5.conf file. */
227     basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir);
228     putenv(krb5_config);
229     free(krb5_config_empty);
230
231     /* Obtain a new Kerberos context with that krb5.conf file. */
232     krb5_free_context(ctx);
233     code = krb5_init_context(&ctx);
234     if (code != 0)
235         bail_krb5(ctx, code, "cannot initialize Kerberos context");
236
237     /* Run all of the tests again.  No need to re-run generic tests. */
238     code = vtable->open(ctx, NULL, &data);
239     is_int(0, code, "Plugin initialization (krb5.conf dictionary)");
240     if (code != 0)
241         bail("cannot continue after plugin initialization failure");
242     for (i = 0; i < ARRAY_SIZE(cracklib_tests); i++)
243         is_password_test(ctx, vtable, data, &cracklib_tests[i]);
244     vtable->close(ctx, data);
245
246     /* Add character class configuration to krb5.conf. */
247     setup_argv[5] = (char *) "require_ascii_printable";
248     setup_argv[6] = (char *) "true";
249     setup_argv[7] = (char *) "require_non_letter";
250     setup_argv[8] = (char *) "true";
251     setup_argv[9] = NULL;
252     run_setup((const char **) setup_argv);
253
254     /* Obtain a new Kerberos context with that krb5.conf file. */
255     krb5_free_context(ctx);
256     code = krb5_init_context(&ctx);
257     if (code != 0)
258         bail_krb5(ctx, code, "cannot initialize Kerberos context");
259
260     /* Run all the character class tests. */
261     code = vtable->open(ctx, NULL, &data);
262     is_int(0, code, "Plugin initialization (character class)");
263     if (code != 0)
264         bail("cannot continue after plugin initialization failure");
265     for (i = 0; i < ARRAY_SIZE(class_tests); i++)
266         is_password_test(ctx, vtable, data, &class_tests[i]);
267     vtable->close(ctx, data);
268
269     /* Add minimum length configuration to krb5.conf. */
270     setup_argv[5] = (char *) "minimum_length";
271     setup_argv[6] = (char *) "12";
272     setup_argv[7] = NULL;
273     run_setup((const char **) setup_argv);
274
275     /* Obtain a new Kerberos context with that krb5.conf file. */
276     krb5_free_context(ctx);
277     code = krb5_init_context(&ctx);
278     if (code != 0)
279         bail_krb5(ctx, code, "cannot initialize Kerberos context");
280
281     /* Run all of the length tests. */
282     code = vtable->open(ctx, NULL, &data);
283     is_int(0, code, "Plugin initialization (length)");
284     if (code != 0)
285         bail("cannot continue after plugin initialization failure");
286     for (i = 0; i < ARRAY_SIZE(length_tests); i++)
287         is_password_test(ctx, vtable, data, &length_tests[i]);
288     vtable->close(ctx, data);
289
290 #ifdef HAVE_CDB
291
292     /* If built with CDB, set up krb5.conf to use a CDB dictionary instead. */
293     free(dictionary);
294     dictionary = test_file_path("data/wordlist.cdb");
295     if (dictionary == NULL)
296         bail("cannot find data/wordlist.cdb in the test suite");
297     setup_argv[3] = (char *) "password_dictionary_cdb";
298     setup_argv[4] = dictionary;
299     setup_argv[5] = NULL;
300     run_setup((const char **) setup_argv);
301     test_file_path_free(setup_argv[0]);
302     test_file_path_free(path);
303
304     /* Obtain a new Kerberos context with that krb5.conf file. */
305     krb5_free_context(ctx);
306     code = krb5_init_context(&ctx);
307     if (code != 0)
308         bail_krb5(ctx, code, "cannot initialize Kerberos context");
309
310     /* Run the CDB and generic tests. */
311     code = vtable->open(ctx, NULL, &data);
312     is_int(0, code, "Plugin initialization (CDB dictionary)");
313     if (code != 0)
314         bail("cannot continue after plugin initialization failure");
315     for (i = 0; i < ARRAY_SIZE(cdb_tests); i++)
316         is_password_test(ctx, vtable, data, &cdb_tests[i]);
317     for (i = 0; i < ARRAY_SIZE(generic_tests); i++)
318         is_password_test(ctx, vtable, data, &generic_tests[i]);
319     vtable->close(ctx, data);
320
321 #else /* !HAVE_CDB */
322
323     /* Otherwise, mark the CDB tests as skipped. */
324     count = ARRAY_SIZE(cdb_tests) + ARRAY_SIZE(generic_tests);
325     skip_block(count * 2 + 1, "not built with CDB support");
326
327 #endif /* !HAVE_CDB */
328
329     /* Manually clean up after the results of make-krb5-conf. */
330     basprintf(&path, "%s/krb5.conf", tmpdir);
331     unlink(path);
332     free(path);
333     test_tmpdir_free(tmpdir);
334
335     /* Keep valgrind clean by freeing all memory. */
336     test_file_path_free(dictionary);
337     krb5_free_context(ctx);
338     free(vtable);
339     putenv((char *) "KRB5_CONFIG=");
340     free(krb5_config);
341     return 0;
342 }
343
344 #endif /* HAVE_KRB5_PWQUAL_PLUGIN_H */