xref: /freebsd/crypto/krb5/src/lib/kadm5/t_kadm5.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kadm5/t_kadm5.c - API tests for libkadm5 */
3 /*
4  * Copyright (C) 2021 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "k5-int.h"
34 #include <kadm5/admin.h>
35 
36 static uint32_t api;
37 static krb5_boolean rpc;
38 
39 static krb5_context context;
40 
41 /* These must match the creation commands in t_kadm5.py. */
42 #define ADMIN_PASSWORD "admin"
43 #define USER_PASSWORD "us3r"
44 
45 /* This list must match the supported_enctypes setting in t_kadm5.py. */
46 static krb5_enctype
47 default_supported_enctypes[] = {
48     ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96,
49     ENCTYPE_NULL
50 };
51 
52 static void
check(krb5_error_code code)53 check(krb5_error_code code)
54 {
55     assert(code == 0);
56 }
57 
58 static void
check_fail(krb5_error_code code,krb5_error_code expected)59 check_fail(krb5_error_code code, krb5_error_code expected)
60 {
61     assert(code == expected);
62 }
63 
64 /*
65  * Initialize a handle using the global context.  The caller must destroy this
66  * handle before initializing another one.  If the client name begins with '$',
67  * authenticate to kadmin/changepw; otherwise authenticate to kadmin/admin.  If
68  * client is null, return a null handle.
69  */
70 static void *
get_handle(char * client)71 get_handle(char *client)
72 {
73     void *handle;
74     char *service, *pass;
75 
76     if (client == NULL)
77         return NULL;
78 
79     if (*client == '$') {
80         service = KADM5_CHANGEPW_SERVICE;
81         client++;
82     } else {
83         service = KADM5_ADMIN_SERVICE;
84     }
85     pass = (strcmp(client, "user") == 0) ? USER_PASSWORD : ADMIN_PASSWORD;
86 
87     check(kadm5_init(context, client, pass, service, NULL,
88                      KADM5_STRUCT_VERSION, api, NULL, &handle));
89     return handle;
90 }
91 
92 static void
free_handle(void * handle)93 free_handle(void *handle)
94 {
95     if (handle != NULL)
96         check(kadm5_destroy(handle));
97 }
98 
99 static krb5_principal
parse_princ(const char * str)100 parse_princ(const char *str)
101 {
102     krb5_principal princ;
103 
104     check(krb5_parse_name(context, str, &princ));
105     return princ;
106 }
107 
108 static void
create_simple_policy(char * name)109 create_simple_policy(char *name)
110 {
111     void *handle = get_handle("admin");
112     kadm5_policy_ent_rec ent;
113 
114     memset(&ent, 0, sizeof(ent));
115     ent.policy = name;
116     check(kadm5_create_policy(handle, &ent, KADM5_POLICY));
117     free_handle(handle);
118 }
119 
120 static void
delete_policy(char * name)121 delete_policy(char *name)
122 {
123     void *handle = get_handle("admin");
124 
125     check(kadm5_delete_policy(handle, name));
126     free_handle(handle);
127 }
128 
129 static void
compare_policy(kadm5_policy_ent_t x,uint32_t mask)130 compare_policy(kadm5_policy_ent_t x, uint32_t mask)
131 {
132     kadm5_policy_ent_rec g;
133     void *handle = get_handle("admin");
134 
135     check(kadm5_get_policy(handle, x->policy, &g));
136 
137     assert(strcmp(g.policy, x->policy) == 0);
138     if (mask & KADM5_PW_MAX_LIFE)
139         assert(g.pw_max_life == x->pw_max_life);
140     if (mask & KADM5_PW_MIN_LIFE)
141         assert(g.pw_min_life == x->pw_min_life);
142     if (mask & KADM5_PW_MIN_LENGTH)
143         assert(g.pw_min_length == x->pw_min_length);
144     if (mask & KADM5_PW_MIN_CLASSES)
145         assert(g.pw_min_classes == x->pw_min_classes);
146     if (mask & KADM5_PW_HISTORY_NUM)
147         assert(g.pw_history_num == x->pw_history_num);
148     if (mask & KADM5_PW_MAX_FAILURE)
149         assert(g.pw_max_fail == x->pw_max_fail);
150     if (mask & KADM5_PW_FAILURE_COUNT_INTERVAL)
151         assert(g.pw_failcnt_interval == x->pw_failcnt_interval);
152     if (mask & KADM5_PW_LOCKOUT_DURATION)
153         assert(g.pw_lockout_duration == x->pw_lockout_duration);
154 
155     check(kadm5_free_policy_ent(handle, &g));
156     free_handle(handle);
157 }
158 
159 static void
create_simple_princ(krb5_principal princ,char * policy)160 create_simple_princ(krb5_principal princ, char *policy)
161 {
162     void *handle = get_handle("admin");
163     kadm5_principal_ent_rec ent;
164     uint32_t mask = KADM5_PRINCIPAL;
165 
166     memset(&ent, 0, sizeof(ent));
167     ent.principal = princ;
168     ent.policy = policy;
169     if (policy != NULL)
170         mask |= KADM5_POLICY;
171     check(kadm5_create_principal(handle, &ent, mask, "pw"));
172     free_handle(handle);
173 }
174 
175 static void
delete_princ(krb5_principal princ)176 delete_princ(krb5_principal princ)
177 {
178     void *handle = get_handle("admin");
179 
180     check(kadm5_delete_principal(handle, princ));
181     free_handle(handle);
182 }
183 
184 static void
compare_key_data(kadm5_principal_ent_t ent,const krb5_enctype * etypes)185 compare_key_data(kadm5_principal_ent_t ent, const krb5_enctype *etypes)
186 {
187     int i;
188 
189     for (i = 0; etypes[i] != ENCTYPE_NULL; i++) {
190         assert(i < ent->n_key_data);
191         assert(ent->key_data[i].key_data_ver >= 1);
192         assert(ent->key_data[i].key_data_type[0] == etypes[i]);
193     }
194 }
195 
196 static void
compare_princ(kadm5_principal_ent_t x,uint32_t mask)197 compare_princ(kadm5_principal_ent_t x, uint32_t mask)
198 {
199     void *handle = get_handle("admin");
200     kadm5_principal_ent_rec g;
201     kadm5_policy_ent_rec pol;
202 
203     check(kadm5_get_principal(handle, x->principal, &g,
204                               KADM5_PRINCIPAL_NORMAL_MASK));
205 
206     assert(krb5_principal_compare(context, g.principal, x->principal));
207     if (mask & KADM5_POLICY)
208         assert(strcmp(g.policy, x->policy) == 0);
209     if (mask & KADM5_PRINC_EXPIRE_TIME)
210         assert(g.princ_expire_time == x->princ_expire_time);
211     if (mask & KADM5_MAX_LIFE)
212         assert(g.max_life == x->max_life);
213     if (mask & KADM5_MAX_RLIFE)
214         assert(g.max_renewable_life == x->max_renewable_life);
215     if (mask & KADM5_FAIL_AUTH_COUNT)
216         assert(g.fail_auth_count == x->fail_auth_count);
217     if (mask & KADM5_ATTRIBUTES)
218         assert(g.attributes == x->attributes);
219     if (mask & KADM5_KVNO)
220         assert(g.kvno == x->kvno);
221 
222     if (mask & KADM5_PW_EXPIRATION) {
223         assert(g.pw_expiration == x->pw_expiration);
224     } else if ((mask & KADM5_POLICY) &&
225                kadm5_get_policy(handle, g.policy, &pol) == 0) {
226         /* Check the policy pw_max_life computation. */
227         if (pol.pw_max_life != 0) {
228             assert(ts_incr(g.last_pwd_change, pol.pw_max_life) ==
229                    g.pw_expiration);
230         } else {
231             assert(g.pw_expiration == 0);
232         }
233         check(kadm5_free_policy_ent(handle, &pol));
234     }
235 
236     if (mask & KADM5_POLICY_CLR) {
237         assert(g.policy == NULL);
238         if (!(mask & KADM5_PW_EXPIRATION))
239             assert(g.pw_expiration == 0);
240     }
241 
242     check(kadm5_free_principal_ent(handle, &g));
243     free_handle(handle);
244 }
245 
246 static void
kinit(krb5_ccache cc,const char * user,const char * pass,const char * service)247 kinit(krb5_ccache cc, const char *user, const char *pass, const char *service)
248 {
249     krb5_get_init_creds_opt *opt;
250     krb5_principal client = parse_princ(user);
251     krb5_creds creds;
252 
253     check(krb5_get_init_creds_opt_alloc(context, &opt));
254     check(krb5_get_init_creds_opt_set_out_ccache(context, opt, cc));
255     check(krb5_get_init_creds_password(context, &creds, client, pass, NULL,
256                                        NULL, 0, service, opt));
257     krb5_get_init_creds_opt_free(context, opt);
258     krb5_free_cred_contents(context, &creds);
259     krb5_free_principal(context, client);
260 }
261 
262 static void
cpw_test_fail(char * user,krb5_principal princ,char * pass,krb5_error_code code)263 cpw_test_fail(char *user, krb5_principal princ, char *pass,
264               krb5_error_code code)
265 {
266     void *handle = get_handle(user);
267 
268     check_fail(kadm5_chpass_principal(handle, princ, pass), code);
269     free_handle(handle);
270 }
271 
272 static void
cpw_test_succeed(char * user,krb5_principal princ,char * pass)273 cpw_test_succeed(char *user, krb5_principal princ, char *pass)
274 {
275     cpw_test_fail(user, princ, pass, 0);
276 }
277 
278 static void
test_chpass()279 test_chpass()
280 {
281     krb5_principal princ = parse_princ("chpass-test");
282     krb5_principal hist_princ = parse_princ("kadmin/history");
283     kadm5_principal_ent_rec ent;
284     void *handle;
285 
286     /* Specify a policy so that kadmin/history is created. */
287     create_simple_princ(princ, "minlife-pol");
288 
289     /* Check kvno and enctypes after a password change. */
290     handle = get_handle("admin");
291     check(kadm5_chpass_principal(handle, princ, "newpassword"));
292     check(kadm5_get_principal(handle, princ, &ent, KADM5_KEY_DATA));
293     compare_key_data(&ent, default_supported_enctypes);
294     assert(ent.key_data[0].key_data_kvno == 2);
295     check(kadm5_free_principal_ent(handle, &ent));
296     free_handle(handle);
297 
298     /* Fails for protected principal. */
299     cpw_test_fail("admin", hist_princ, "pw", KADM5_PROTECT_PRINCIPAL);
300 
301     /* Fails over RPC if "change" ACL is not granted, or if we authenticated to
302      * kadmin/changepw and are changing another principal's password. */
303     if (rpc) {
304         cpw_test_succeed("admin/modify", princ, "pw2");
305         cpw_test_fail("admin/none", princ, "pw3", KADM5_AUTH_CHANGEPW);
306         cpw_test_fail("$admin", princ, "pw3", KADM5_AUTH_CHANGEPW);
307     }
308 
309     /* Fails with null handle or principal name. */
310     cpw_test_fail(NULL, princ, "pw", KADM5_BAD_SERVER_HANDLE);
311     cpw_test_fail("admin", NULL, "pw", EINVAL);
312 
313     delete_princ(princ);
314     krb5_free_principal(context, princ);
315     krb5_free_principal(context, hist_princ);
316 }
317 
318 static void
cpol_test_fail(char * user,kadm5_policy_ent_t ent,uint32_t mask,krb5_error_code code)319 cpol_test_fail(char *user, kadm5_policy_ent_t ent, uint32_t mask,
320                krb5_error_code code)
321 {
322     void *handle = get_handle(user);
323 
324     check_fail(kadm5_create_policy(handle, ent, mask | KADM5_POLICY), code);
325     free_handle(handle);
326 }
327 
328 static void
cpol_test_compare(char * user,kadm5_policy_ent_t ent,uint32_t mask)329 cpol_test_compare(char *user, kadm5_policy_ent_t ent, uint32_t mask)
330 {
331     cpol_test_fail(user, ent, mask, 0);
332     compare_policy(ent, mask);
333     delete_policy(ent->policy);
334 }
335 
336 static void
test_create_policy()337 test_create_policy()
338 {
339     void *handle;
340     kadm5_policy_ent_rec ent;
341 
342     memset(&ent, 0, sizeof(ent));
343 
344     /* Fails with undefined mask bit. */
345     ent.policy = "create-policy-test";
346     cpol_test_fail("admin", &ent, 0x10000000, KADM5_BAD_MASK);
347 
348     /* Fails without KADM5_POLICY mask bit. */
349     handle = get_handle("admin");
350     check_fail(kadm5_create_policy(handle, &ent, 0), KADM5_BAD_MASK);
351     free_handle(handle);
352 
353     /* pw_min_life = 0 and pw_min_life != 0 */
354     cpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
355     ent.pw_min_life = 32;
356     cpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
357 
358     /* pw_max_life = 0 and pw_max_life != 0 */
359     cpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
360     ent.pw_max_life = 32;
361     cpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
362 
363     /* pw_min_length = 0 (rejected) and pw_min_length != 0 */
364     cpol_test_fail("admin", &ent, KADM5_PW_MIN_LENGTH, KADM5_BAD_LENGTH);
365     ent.pw_min_length = 32;
366     cpol_test_compare("admin", &ent, KADM5_PW_MIN_LENGTH);
367 
368     /* pw_min_classes = 0 (rejected), 1, 5, 6 (rejected) */
369     cpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
370     ent.pw_min_classes = 1;
371     cpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
372     ent.pw_min_classes = 5;
373     cpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
374     ent.pw_min_classes = 6;
375     cpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
376 
377     /* pw_history_num = 0 (rejected), 1, 10 */
378     cpol_test_fail("admin", &ent, KADM5_PW_HISTORY_NUM, KADM5_BAD_HISTORY);
379     ent.pw_history_num = 1;
380     cpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
381     ent.pw_history_num = 10;
382     cpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
383 
384     if (api >= KADM5_API_VERSION_3) {
385         ent.pw_max_fail = 2;
386         cpol_test_compare("admin", &ent, KADM5_PW_MAX_FAILURE);
387         ent.pw_failcnt_interval = 90;
388         cpol_test_compare("admin", &ent,
389                           KADM5_PW_FAILURE_COUNT_INTERVAL);
390         ent.pw_lockout_duration = 180;
391         cpol_test_compare("admin", &ent, KADM5_PW_LOCKOUT_DURATION);
392     }
393 
394     /* Fails over RPC if "add" ACL is not granted, or if we authenticated to
395      * kadmin/changepw. */
396     if (rpc) {
397         cpol_test_fail("$admin", &ent, 0, KADM5_AUTH_ADD);
398         cpol_test_fail("admin/none", &ent, 0, KADM5_AUTH_ADD);
399         cpol_test_fail("admin/get", &ent, 0, KADM5_AUTH_ADD);
400         cpol_test_fail("admin/modify", &ent, 0, KADM5_AUTH_ADD);
401         cpol_test_fail("admin/delete", &ent, 0, KADM5_AUTH_ADD);
402         cpol_test_compare("admin/add", &ent, 0);
403     }
404 
405     /* Fails with existing policy name. */
406     ent.policy = "test-pol";
407     cpol_test_fail("admin", &ent, 0, KADM5_DUP);
408 
409     /* Fails with null or empty policy name, or invalid character in name. */
410     ent.policy = NULL;
411     cpol_test_fail("admin", &ent, 0, EINVAL);
412     ent.policy = "";
413     cpol_test_fail("admin", &ent, 0, KADM5_BAD_POLICY);
414     ent.policy = "pol\7";
415     cpol_test_fail("admin", &ent, 0, KADM5_BAD_POLICY);
416 
417     /* Fails with null handle or policy ent. */
418     cpol_test_fail(NULL, &ent, 0, KADM5_BAD_SERVER_HANDLE);
419     cpol_test_fail("admin", NULL, 0, EINVAL);
420 }
421 
422 static void
cprinc_test_fail(char * user,kadm5_principal_ent_t ent,uint32_t mask,char * pass,krb5_error_code code)423 cprinc_test_fail(char *user, kadm5_principal_ent_t ent, uint32_t mask,
424                  char *pass, krb5_error_code code)
425 {
426     void *handle = get_handle(user);
427 
428     check_fail(kadm5_create_principal(handle, ent, mask | KADM5_PRINCIPAL,
429                                       pass), code);
430     free_handle(handle);
431 }
432 
433 static void
cprinc_test_compare(char * user,kadm5_principal_ent_t ent,uint32_t mask,char * pass)434 cprinc_test_compare(char *user, kadm5_principal_ent_t ent, uint32_t mask,
435                     char *pass)
436 {
437     cprinc_test_fail(user, ent, mask, pass, 0);
438     compare_princ(ent, mask);
439     delete_princ(ent->principal);
440 }
441 
442 static void
test_create_principal()443 test_create_principal()
444 {
445     void *handle;
446     kadm5_principal_ent_rec ent;
447     krb5_principal princ = parse_princ("create-principal-test");
448     krb5_principal user_princ = parse_princ("user");
449 
450     memset(&ent, 0, sizeof(ent));
451     ent.principal = princ;
452 
453     /* Fails with undefined or prohibited mask bit. */
454     cprinc_test_fail("admin", &ent, 0x100000, "", KADM5_BAD_MASK);
455     cprinc_test_fail("admin", &ent, KADM5_LAST_PWD_CHANGE, "pw",
456                      KADM5_BAD_MASK);
457     cprinc_test_fail("admin", &ent, KADM5_MOD_TIME, "pw", KADM5_BAD_MASK);
458     cprinc_test_fail("admin", &ent, KADM5_MOD_NAME, "pw", KADM5_BAD_MASK);
459     cprinc_test_fail("admin", &ent, KADM5_MKVNO, "pw", KADM5_BAD_MASK);
460     cprinc_test_fail("admin", &ent, KADM5_AUX_ATTRIBUTES, "pw",
461                      KADM5_BAD_MASK);
462 
463     /* Fails without KADM5_PRINCIPAL mask bit. */
464     handle = get_handle("admin");
465     check_fail(kadm5_create_principal(handle, &ent, 0, "pw"), KADM5_BAD_MASK);
466     free_handle(handle);
467 
468     /* Fails with empty password or password prohibited by policy. */
469     cprinc_test_fail("admin", &ent, 0, "", KADM5_PASS_Q_TOOSHORT);
470     ent.policy = "test-pol";
471     cprinc_test_fail("admin", &ent, KADM5_POLICY, "tP", KADM5_PASS_Q_TOOSHORT);
472     cprinc_test_fail("admin", &ent, KADM5_POLICY, "testpassword",
473                      KADM5_PASS_Q_CLASS);
474     cprinc_test_fail("admin", &ent, KADM5_POLICY, "Abyssinia",
475                      KADM5_PASS_Q_DICT);
476 
477     cprinc_test_compare("admin", &ent, 0, "pw");
478     ent.policy = "nonexistent-pol";
479     cprinc_test_compare("admin", &ent, KADM5_POLICY, "pw");
480     cprinc_test_compare("admin/rename", &ent, KADM5_POLICY, "pw");
481 
482     /* Test pw_expiration explicit specifications vs. policy pw_max_life. */
483     ent.policy = "test-pol";
484     cprinc_test_compare("admin", &ent, KADM5_POLICY, "NotinTheDictionary");
485     cprinc_test_compare("admin", &ent, KADM5_PRINC_EXPIRE_TIME, "pw");
486     cprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION, "pw");
487     cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
488                         "NotinTheDictionary");
489     ent.pw_expiration = 1234;
490     cprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION, "pw");
491     cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
492                         "NotinTheDictionary");
493     ent.pw_expiration = 999999999;
494     cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
495                         "NotinTheDictionary");
496     ent.policy = "dict-only-pol";
497     cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
498                         "pw");
499 
500     /* Fails over RPC if "add" ACL is not granted, or if we authenticated to
501      * kadmin/changepw. */
502     if (rpc) {
503         cprinc_test_fail("$admin", &ent, 0, "pw", KADM5_AUTH_ADD);
504         cprinc_test_fail("admin/none", &ent, 0, "pw", KADM5_AUTH_ADD);
505         cprinc_test_fail("admin/get", &ent, 0, "pw", KADM5_AUTH_ADD);
506         cprinc_test_fail("admin/modify", &ent, 0, "pw", KADM5_AUTH_ADD);
507         cprinc_test_fail("admin/delete", &ent, 0, "pw", KADM5_AUTH_ADD);
508     }
509 
510     /* Fails with existing policy name. */
511     ent.principal = user_princ;
512     cprinc_test_fail("admin", &ent, 0, "pw", KADM5_DUP);
513 
514     /* Fails with null handle or principal ent. */
515     cprinc_test_fail(NULL, &ent, 0, "pw", KADM5_BAD_SERVER_HANDLE);
516     cprinc_test_fail("admin", NULL, 0, "pw", EINVAL);
517 
518     krb5_free_principal(context, princ);
519     krb5_free_principal(context, user_princ);
520 }
521 
522 static void
dpol_test_fail(char * user,char * name,krb5_error_code code)523 dpol_test_fail(char *user, char *name, krb5_error_code code)
524 {
525     void *handle = get_handle(user);
526 
527     check_fail(kadm5_delete_policy(handle, name), code);
528     free_handle(handle);
529 }
530 
531 static void
dpol_test_succeed(char * user,char * name)532 dpol_test_succeed(char *user, char *name)
533 {
534     dpol_test_fail(user, name, 0);
535 }
536 
537 static void
test_delete_policy()538 test_delete_policy()
539 {
540     krb5_principal princ = parse_princ("delete-policy-test-princ");
541 
542     /* Fails with unknown policy. */
543     dpol_test_fail("admin", "delete-policy-test", KADM5_UNK_POLICY);
544 
545     /* Fails with empty policy name. */
546     dpol_test_fail("admin", "", KADM5_BAD_POLICY);
547 
548     /* Succeeds with "delete" ACL (or local authentication). */
549     create_simple_policy("delete-policy-test");
550     dpol_test_succeed("admin/delete", "delete-policy-test");
551 
552     /* Succeeds even if a principal references the policy, since we now allow
553      * principals to reference nonexistent policies. */
554     create_simple_policy("delete-policy-test");
555     create_simple_princ(princ, "delete-policy-test");
556     dpol_test_succeed("admin", "delete-policy-test");
557     delete_princ(princ);
558 
559     /* Fails over RPC if "delete" ACL is not granted, or if we authenticated to
560      * kadmin/changepw. */
561     if (rpc) {
562         dpol_test_fail("$admin", "test-pol", KADM5_AUTH_DELETE);
563         dpol_test_fail("admin/none", "test-pol", KADM5_AUTH_DELETE);
564         dpol_test_fail("admin/add", "test-pol", KADM5_AUTH_DELETE);
565     }
566 
567     /* Fails with null handle or principal ent. */
568     dpol_test_fail(NULL, "test-pol", KADM5_BAD_SERVER_HANDLE);
569     dpol_test_fail("admin", NULL, EINVAL);
570 
571     krb5_free_principal(context, princ);
572 }
573 
574 static void
dprinc_test_fail(char * user,krb5_principal princ,krb5_error_code code)575 dprinc_test_fail(char *user, krb5_principal princ, krb5_error_code code)
576 {
577     void *handle = get_handle(user);
578 
579     check_fail(kadm5_delete_principal(handle, princ), code);
580     free_handle(handle);
581 }
582 
583 static void
dprinc_test_succeed(char * user,krb5_principal princ)584 dprinc_test_succeed(char *user, krb5_principal princ)
585 {
586     dprinc_test_fail(user, princ, 0);
587 }
588 
589 static void
test_delete_principal()590 test_delete_principal()
591 {
592     krb5_principal princ = parse_princ("delete-principal-test");
593 
594     /* Fails with unknown principal. */
595     dprinc_test_fail("admin", princ, KADM5_UNK_PRINC);
596 
597     /* Succeeds with "delete" ACL (or local authentication). */
598     create_simple_princ(princ, NULL);
599     dprinc_test_succeed("admin/delete", princ);
600 
601     /* Fails over RPC if "delete" ACL is not granted, or if we authenticated to
602      * kadmin/changepw. */
603     if (rpc) {
604         dprinc_test_fail("$admin", princ, KADM5_AUTH_DELETE);
605         dprinc_test_fail("admin/add", princ, KADM5_AUTH_DELETE);
606         dprinc_test_fail("admin/modify", princ, KADM5_AUTH_DELETE);
607         dprinc_test_fail("admin/get", princ, KADM5_AUTH_DELETE);
608         dprinc_test_fail("admin/none", princ, KADM5_AUTH_DELETE);
609     }
610 
611     /* Fails with null handle or principal ent. */
612     dprinc_test_fail(NULL, princ, KADM5_BAD_SERVER_HANDLE);
613     dprinc_test_fail("admin", NULL, EINVAL);
614 
615     krb5_free_principal(context, princ);
616 }
617 
618 static void
gpol_test_succeed(char * user,char * name)619 gpol_test_succeed(char *user, char *name)
620 {
621     void *handle = get_handle(user);
622     kadm5_policy_ent_rec ent;
623 
624     check(kadm5_get_policy(handle, name, &ent));
625     assert(strcmp(ent.policy, name) == 0);
626     check(kadm5_free_policy_ent(handle, &ent));
627     free_handle(handle);
628 }
629 
630 static void
gpol_test_fail(char * user,char * name,krb5_error_code code)631 gpol_test_fail(char *user, char *name, krb5_error_code code)
632 {
633     void *handle = get_handle(user);
634     kadm5_policy_ent_rec ent;
635 
636     check_fail(kadm5_get_policy(handle, name, &ent), code);
637     free_handle(handle);
638 }
639 
640 static void
test_get_policy()641 test_get_policy()
642 {
643     /* Fails with unknown policy. */
644     dpol_test_fail("admin", "unknown-policy", KADM5_UNK_POLICY);
645 
646     /* Fails with empty or null policy name or a null handle. */
647     gpol_test_fail("admin", "", KADM5_BAD_POLICY);
648     gpol_test_fail("admin", NULL, EINVAL);
649     gpol_test_fail(NULL, "", KADM5_BAD_SERVER_HANDLE);
650 
651     /* Fails over RPC unless "get" ACL is granted or the principal's own policy
652      * is retrieved. */
653     if (rpc) {
654         gpol_test_fail("admin/none", "test-pol", KADM5_AUTH_GET);
655         gpol_test_fail("admin/add", "test-pol", KADM5_AUTH_GET);
656         gpol_test_succeed("admin/get", "test-pol");
657         gpol_test_succeed("user", "minlife-pol");
658         gpol_test_succeed("$user", "minlife-pol");
659     }
660 }
661 
662 static void
gprinc_test_succeed(char * user,krb5_principal princ)663 gprinc_test_succeed(char *user, krb5_principal princ)
664 {
665     void *handle = get_handle(user);
666     kadm5_principal_ent_rec ent;
667 
668     check(kadm5_get_principal(handle, princ, &ent,
669                               KADM5_PRINCIPAL_NORMAL_MASK));
670     assert(krb5_principal_compare(context, ent.principal, princ));
671     check(kadm5_free_principal_ent(handle, &ent));
672     free_handle(handle);
673 }
674 
675 static void
gprinc_test_fail(char * user,krb5_principal princ,krb5_error_code code)676 gprinc_test_fail(char *user, krb5_principal princ, krb5_error_code code)
677 {
678     void *handle = get_handle(user);
679     kadm5_principal_ent_rec ent;
680 
681     check_fail(kadm5_get_principal(handle, princ, &ent,
682                                    KADM5_PRINCIPAL_NORMAL_MASK), code);
683     free_handle(handle);
684 }
685 
686 static void
test_get_principal()687 test_get_principal()
688 {
689     void *handle;
690     kadm5_principal_ent_rec ent;
691     krb5_principal princ = parse_princ("get-principal-test");
692     krb5_principal admin_princ = parse_princ("admin");
693     krb5_principal admin_none_princ = parse_princ("admin/none");
694     int i;
695 
696     /* Fails with unknown principal. */
697     gprinc_test_fail("admin", princ, KADM5_UNK_PRINC);
698 
699     create_simple_princ(princ, NULL);
700 
701     /* Succeeds with "get" ACL (or local authentication), or operating on
702      * self. */
703     gprinc_test_succeed("admin/none", admin_none_princ);
704     gprinc_test_succeed("$admin", admin_princ);
705     gprinc_test_succeed("admin/get", princ);
706 
707     /* Fails over RPC if "get" ACL is not granted, or if we authenticated to
708      * kadmin/changepw and getting another principal entry. */
709     if (rpc) {
710         gprinc_test_fail("$admin", princ, KADM5_AUTH_GET);
711         gprinc_test_fail("admin/none", princ, KADM5_AUTH_GET);
712         gprinc_test_fail("admin/add", princ, KADM5_AUTH_GET);
713         gprinc_test_fail("admin/modify", princ, KADM5_AUTH_GET);
714         gprinc_test_fail("admin/delete", princ, KADM5_AUTH_GET);
715     }
716 
717     /* Entry contains no key data or tl-data unless asked for. */
718     handle = get_handle("admin");
719     check(kadm5_get_principal(handle, princ, &ent,
720                               KADM5_PRINCIPAL_NORMAL_MASK));
721     assert(ent.n_tl_data == 0);
722     assert(ent.n_key_data == 0);
723     assert(ent.tl_data == NULL);
724     check(kadm5_free_principal_ent(handle, &ent));
725 
726     /* Key data (without the actual keys over RPC) is provided if asked for. */
727     check(kadm5_get_principal(handle, princ, &ent,
728                               KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA));
729     assert(ent.n_key_data == 2);
730     for (i = 0; i < ent.n_key_data; i++)
731         assert(rpc == (ent.key_data[i].key_data_length[0] == 0));
732     check(kadm5_free_principal_ent(handle, &ent));
733     free_handle(handle);
734 
735     /* Fails with null handle or principal. */
736     gprinc_test_fail(NULL, princ, KADM5_BAD_SERVER_HANDLE);
737     gprinc_test_fail("admin", NULL, EINVAL);
738 
739     delete_princ(princ);
740     krb5_free_principal(context, princ);
741     krb5_free_principal(context, admin_princ);
742     krb5_free_principal(context, admin_none_princ);
743 }
744 
745 static void
test_init_destroy()746 test_init_destroy()
747 {
748     krb5_context ctx;
749     kadm5_ret_t ret;
750     kadm5_config_params params;
751     kadm5_principal_ent_rec ent, gent;
752     krb5_principal princ = parse_princ("init-test");
753     krb5_ccache cc;
754     void *handle;
755     char hostname[MAXHOSTNAMELEN];
756     int r;
757 
758     memset(&params, 0, sizeof(params));
759     memset(&ent, 0, sizeof(ent));
760     ent.principal = princ;
761 
762     r = gethostname(hostname, sizeof(hostname));
763     assert(r == 0);
764 
765     /* Destroy fails with no server handle. */
766     check_fail(kadm5_destroy(NULL), KADM5_BAD_SERVER_HANDLE);
767 
768     /* Fails with bad structure version mask. */
769     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
770                           0x65432101, api, NULL, &handle),
771                KADM5_BAD_STRUCT_VERSION);
772     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
773                           1, api, NULL, &handle), KADM5_BAD_STRUCT_VERSION);
774 
775     /* Fails with too-old or too-new structure version. */
776     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
777                           KADM5_STRUCT_VERSION_MASK, api, NULL, &handle),
778                KADM5_OLD_STRUCT_VERSION);
779     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
780                           KADM5_STRUCT_VERSION_MASK | 0xca, api, NULL,
781                           &handle), KADM5_NEW_STRUCT_VERSION);
782 
783     /* Fails with bad API version mask. */
784     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
785                           KADM5_STRUCT_VERSION, 0x65432100, NULL, &handle),
786                KADM5_BAD_API_VERSION);
787     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
788                           KADM5_STRUCT_VERSION, 4, NULL, &handle),
789                KADM5_BAD_API_VERSION);
790 
791     /* Fails with too-old or too-new API version.*/
792     ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
793                      KADM5_STRUCT_VERSION, KADM5_API_VERSION_MASK, NULL,
794                      &handle);
795     assert(ret == (rpc ? KADM5_OLD_LIB_API_VERSION :
796                    KADM5_OLD_SERVER_API_VERSION));
797     ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
798                      KADM5_STRUCT_VERSION, KADM5_API_VERSION_MASK | 0xca, NULL,
799                      &handle);
800     assert(ret == (rpc ? KADM5_NEW_LIB_API_VERSION :
801                    KADM5_NEW_SERVER_API_VERSION));
802 
803     /* Fails with structure and API version reversed. */
804     check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
805                           api, KADM5_STRUCT_VERSION, NULL, &handle),
806                KADM5_BAD_STRUCT_VERSION);
807 
808     /* Hardcoded default max lifetime is used when no handle or krb5.conf
809      * setting is given. */
810     handle = get_handle("admin");
811     check(kadm5_create_principal(handle, &ent, KADM5_PRINCIPAL, "pw"));
812     check(kadm5_get_principal(handle, princ, &gent,
813                               KADM5_PRINCIPAL_NORMAL_MASK));
814     assert(gent.max_life == KRB5_KDB_MAX_LIFE);
815     check(kadm5_delete_principal(handle, princ));
816     check(kadm5_free_principal_ent(handle, &gent));
817     free_handle(handle);
818 
819     /* Fails with configured unknown realm.  Do these tests in separate krb5
820      * contexts since the realm setting sticks to the context. */
821     check(kadm5_init_krb5_context(&ctx));
822     params.realm = "";
823     params.mask = KADM5_CONFIG_REALM;
824     ret = kadm5_init(ctx, "admin", "admin", KADM5_ADMIN_SERVICE, &params,
825                      KADM5_STRUCT_VERSION, api, NULL, &handle);
826     assert(ret == (rpc ? KADM5_MISSING_KRB5_CONF_PARAMS : ENOENT));
827     krb5_free_context(ctx);
828 
829     check(kadm5_init_krb5_context(&ctx));
830     params.realm = "@";
831     ret = kadm5_init(ctx, "admin", "admin", KADM5_ADMIN_SERVICE, &params,
832                      KADM5_STRUCT_VERSION, api, NULL, &handle);
833     assert(ret == (rpc ? KADM5_MISSING_KRB5_CONF_PARAMS : ENOENT));
834     krb5_free_context(ctx);
835 
836     check(kadm5_init_krb5_context(&ctx));
837     params.realm = "BAD.REALM";
838     ret = kadm5_init(ctx, "admin", "admin", KADM5_ADMIN_SERVICE, &params,
839                      KADM5_STRUCT_VERSION, api, NULL, &handle);
840     assert(ret == (rpc ? KADM5_MISSING_KRB5_CONF_PARAMS : ENOENT));
841     krb5_free_context(ctx);
842 
843     /* Succeeds with explicit client realm and configured realm. */
844     check(kadm5_init_krb5_context(&ctx));
845     params.realm = "KRBTEST.COM";
846     check(kadm5_init(ctx, "admin@KRBTEST.COM", "admin", KADM5_ADMIN_SERVICE,
847                      &params, KADM5_STRUCT_VERSION, api, NULL, &handle));
848     check(kadm5_destroy(handle));
849     krb5_free_context(ctx);
850 
851     /* Succeeds with explicit client realm. */
852     check(kadm5_init(context, "admin@KRBTEST.COM", "admin",
853                      KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION, api,
854                      NULL, &handle));
855     check(kadm5_destroy(handle));
856 
857 
858     if (rpc) {
859         check(krb5_cc_default(context, &cc));
860 
861         /* Succeeds with configured host and port. */
862         params.admin_server = hostname;
863         params.kadmind_port = 61001;
864         params.mask = KADM5_CONFIG_ADMIN_SERVER | KADM5_CONFIG_KADMIND_PORT;
865         check(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
866                          &params, KADM5_STRUCT_VERSION, api, NULL, &handle));
867         check(kadm5_destroy(handle));
868 
869         /* Fails with wrong configured port. */
870         params.kadmind_port = 4;
871         check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
872                               &params, KADM5_STRUCT_VERSION, api, NULL,
873                               &handle), KADM5_RPC_ERROR);
874 
875         /* Fails with non-resolving hostname. */
876         params.admin_server = "does.not.exist";
877         params.mask = KADM5_CONFIG_ADMIN_SERVER;
878         check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
879                               &params, KADM5_STRUCT_VERSION, api, NULL,
880                               &handle), KADM5_CANT_RESOLVE);
881 
882         /* Fails with uninitialized cache. */
883         check_fail(kadm5_init_with_creds(context, "admin", cc,
884                                          KADM5_ADMIN_SERVICE, NULL,
885                                          KADM5_STRUCT_VERSION, api, NULL,
886                                          &handle), KRB5_FCC_NOFILE);
887 
888         /* Succeeds with cache containing kadmin/admin cred. */
889         kinit(cc, "admin", "admin", KADM5_ADMIN_SERVICE);
890         check(kadm5_init_with_creds(context, "admin", cc, KADM5_ADMIN_SERVICE,
891                                     NULL, KADM5_STRUCT_VERSION, api, NULL,
892                                     &handle));
893         check(kadm5_destroy(handle));
894 
895         /* Succeeds with cache containing kadmin/changepw cred. */
896         kinit(cc, "admin", "admin", KADM5_CHANGEPW_SERVICE);
897         check(kadm5_init_with_creds(context, "admin", cc,
898                                     KADM5_CHANGEPW_SERVICE, NULL,
899                                     KADM5_STRUCT_VERSION, api, NULL, &handle));
900         check(kadm5_destroy(handle));
901 
902         /* Fails with cache containing only a TGT. */
903         kinit(cc, "admin", "admin", NULL);
904         check_fail(kadm5_init_with_creds(context, "admin", cc,
905                                          KADM5_ADMIN_SERVICE, NULL,
906                                          KADM5_STRUCT_VERSION, api, NULL,
907                                          &handle), KRB5_CC_NOTFOUND);
908 
909         /* Fails authenticating to non-kadmin princ. */
910         check_fail(kadm5_init(context, "admin", "admin", "user", NULL,
911                               KADM5_STRUCT_VERSION, api, NULL, &handle),
912                    KADM5_RPC_ERROR);
913 
914         /* Fails authenticating to nonexistent princ. */
915         check_fail(kadm5_init(context, "admin", "admin", "noexist", NULL,
916                               KADM5_STRUCT_VERSION, api, NULL, &handle),
917                    KADM5_SECURE_PRINC_MISSING);
918 
919         /* Fails authenticating to client princ (which is non-kadmin). */
920         check_fail(kadm5_init(context, "admin", "admin", "admin", NULL,
921                               KADM5_STRUCT_VERSION, api, NULL, &handle),
922                    KADM5_RPC_ERROR);
923 
924         /* Fails with wrong password. */
925         check_fail(kadm5_init(context, "admin", "wrong", KADM5_ADMIN_SERVICE,
926                               NULL, KADM5_STRUCT_VERSION, api, NULL, &handle),
927                    KADM5_BAD_PASSWORD);
928 
929         /* Fails with null client name. */
930         check_fail(kadm5_init(context, NULL, "admin", KADM5_ADMIN_SERVICE,
931                               NULL, KADM5_STRUCT_VERSION, api, NULL, &handle),
932                    EINVAL);
933 
934         /* Fails with nonexistent client name. */
935         check_fail(kadm5_init(context, "noexist", "admin", KADM5_ADMIN_SERVICE,
936                               NULL, KADM5_STRUCT_VERSION, api, NULL, &handle),
937                    KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN);
938 
939         /* Fails with nonexistent client name with explicit realm. */
940         check_fail(kadm5_init(context, "noexist@KRBTEST.COM", "admin",
941                               KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION,
942                               api, NULL, &handle),
943                    KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN);
944 
945         /* Fails with nonexistent client name with unknown realm. */
946         check_fail(kadm5_init(context, "noexist@BAD.REALM", "admin",
947                               KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION,
948                               api, NULL, &handle), KRB5_REALM_UNKNOWN);
949 
950         /* Fails with known name but unknown realm. */
951         check_fail(kadm5_init(context, "admin@BAD.REALM", "admin",
952                               KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION,
953                               api, NULL, &handle), KRB5_REALM_UNKNOWN);
954 
955         check(krb5_cc_destroy(context, cc));
956     } else {
957         /* Fails with nonexistent stash file. */
958         params.stash_file = "does/not/exist";
959         params.mask = KADM5_CONFIG_STASH_FILE;
960         check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
961                               &params, KADM5_STRUCT_VERSION, api, NULL,
962                               &handle), KRB5_KDB_CANTREAD_STORED);
963 
964         /* Uses configured defaults for principal creation. */
965         params.max_life = 10;
966         params.max_rlife = 20;
967         params.expiration = 30;
968         params.num_keysalts = 0;
969         params.mask = KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE |
970             KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES;
971         check(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
972                          &params, KADM5_STRUCT_VERSION, api, NULL, &handle));
973         check(kadm5_create_principal(handle, &ent, KADM5_PRINCIPAL, "pw"));
974         check(kadm5_get_principal(handle, princ, &gent,
975                                   KADM5_PRINCIPAL_NORMAL_MASK |
976                                   KADM5_KEY_DATA));
977         assert(gent.max_life == 10);
978         assert(gent.max_renewable_life == 20);
979         assert(gent.princ_expire_time == 30);
980         assert(gent.n_key_data == 0);
981         check(kadm5_delete_principal(handle, princ));
982         check(kadm5_free_principal_ent(handle, &gent));
983         check(kadm5_destroy(handle));
984 
985         /* Succeeds with incorrect password using local auth. */
986         check(kadm5_init(context, "admin", "wrong", KADM5_ADMIN_SERVICE, NULL,
987                          KADM5_STRUCT_VERSION, api, NULL, &handle));
988         check(kadm5_destroy(handle));
989 
990         /* Succeeds with null service using local auth. */
991         check(kadm5_init(context, "admin", "admin", NULL, NULL,
992                          KADM5_STRUCT_VERSION, api, NULL, &handle));
993         check(kadm5_destroy(handle));
994 
995         /* Succeeds with nonexistent, non-kadmin service using local auth. */
996         check(kadm5_init(context, "admin", "admin", "foobar", NULL,
997                          KADM5_STRUCT_VERSION, api, NULL, &handle));
998         check(kadm5_destroy(handle));
999     }
1000 
1001     krb5_free_principal(context, princ);
1002 }
1003 
1004 static void
mpol_test_fail(char * user,kadm5_policy_ent_t ent,uint32_t mask,krb5_error_code code)1005 mpol_test_fail(char *user, kadm5_policy_ent_t ent, uint32_t mask,
1006                krb5_error_code code)
1007 {
1008     void *handle = get_handle(user);
1009 
1010     check_fail(kadm5_modify_policy(handle, ent, mask), code);
1011     free_handle(handle);
1012 }
1013 
1014 static void
mpol_test_compare(void * handle,kadm5_policy_ent_t ent,uint32_t mask)1015 mpol_test_compare(void *handle, kadm5_policy_ent_t ent, uint32_t mask)
1016 {
1017     mpol_test_fail(handle, ent, mask, 0);
1018     compare_policy(ent, mask);
1019 }
1020 
1021 static void
test_modify_policy()1022 test_modify_policy()
1023 {
1024     kadm5_policy_ent_rec ent;
1025 
1026     memset(&ent, 0, sizeof(ent));
1027     ent.policy = "modify-policy-test";
1028     create_simple_policy(ent.policy);
1029 
1030     /* pw_min_life = 0 and pw_min_life != 0 */
1031     mpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
1032     ent.pw_min_life = 32;
1033     mpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
1034 
1035     /* pw_max_life = 0 and pw_max_life != 0 */
1036     mpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
1037     ent.pw_max_life = 32;
1038     mpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
1039 
1040     /* pw_min_length = 0 (rejected) and pw_min_length != 0 */
1041     mpol_test_fail("admin", &ent, KADM5_PW_MIN_LENGTH, KADM5_BAD_LENGTH);
1042     ent.pw_min_length = 8;
1043     mpol_test_compare("admin", &ent, KADM5_PW_MIN_LENGTH);
1044 
1045     /* pw_min_classes = 0 (rejected), 1, 5, 6 (rejected) */
1046     mpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
1047     ent.pw_min_classes = 1;
1048     mpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
1049     ent.pw_min_classes = 5;
1050     mpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
1051     ent.pw_min_classes = 6;
1052     mpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
1053 
1054     /* pw_history_num = 0 (rejected), 1, 10 */
1055     mpol_test_fail("admin", &ent, KADM5_PW_HISTORY_NUM, KADM5_BAD_HISTORY);
1056     ent.pw_history_num = 1;
1057     mpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
1058     ent.pw_history_num = 10;
1059     mpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
1060 
1061     if (api >= KADM5_API_VERSION_3) {
1062         ent.pw_max_fail = 2;
1063         mpol_test_compare("admin", &ent, KADM5_PW_MAX_FAILURE);
1064         ent.pw_failcnt_interval = 90;
1065         mpol_test_compare("admin", &ent, KADM5_PW_FAILURE_COUNT_INTERVAL);
1066         ent.pw_lockout_duration = 180;
1067         mpol_test_compare("admin", &ent, KADM5_PW_LOCKOUT_DURATION);
1068     }
1069 
1070     /* Fails over RPC if "modify" ACL is not granted, or if we authenticated to
1071      * kadmin/changepw. */
1072     if (rpc) {
1073         mpol_test_fail("$admin", &ent, KADM5_PW_MAX_LIFE, KADM5_AUTH_MODIFY);
1074         mpol_test_fail("admin/none", &ent, KADM5_PW_MAX_LIFE,
1075                        KADM5_AUTH_MODIFY);
1076         mpol_test_fail("admin/get", &ent, KADM5_PW_MAX_LIFE,
1077                        KADM5_AUTH_MODIFY);
1078         mpol_test_compare("admin/modify", &ent, KADM5_PW_MAX_LIFE);
1079     }
1080 
1081     delete_policy(ent.policy);
1082 
1083     /* Fails with empty or null policy name. */
1084     ent.policy = NULL;
1085     mpol_test_fail("admin", &ent, KADM5_PW_MAX_LIFE, EINVAL);
1086     ent.policy = "";
1087     mpol_test_fail("admin", &ent, KADM5_PW_MAX_LIFE, KADM5_BAD_POLICY);
1088 
1089     /* Fails with null handle or policy ent. */
1090     mpol_test_fail(NULL, &ent, KADM5_PW_MAX_LIFE, KADM5_BAD_SERVER_HANDLE);
1091     mpol_test_fail("admin", NULL, KADM5_PW_MAX_LIFE, EINVAL);
1092 }
1093 
1094 static void
mprinc_test_fail(char * user,kadm5_principal_ent_t ent,uint32_t mask,krb5_error_code code)1095 mprinc_test_fail(char *user, kadm5_principal_ent_t ent, uint32_t mask,
1096                  krb5_error_code code)
1097 {
1098     void *handle = get_handle(user);
1099 
1100     check_fail(kadm5_modify_principal(handle, ent, mask), code);
1101     free_handle(handle);
1102 }
1103 
1104 static void
mprinc_test_compare(char * user,kadm5_principal_ent_t ent,uint32_t mask)1105 mprinc_test_compare(char *user, kadm5_principal_ent_t ent, uint32_t mask)
1106 {
1107     mprinc_test_fail(user, ent, mask, 0);
1108     compare_princ(ent, mask);
1109 }
1110 
1111 static void
test_modify_principal()1112 test_modify_principal()
1113 {
1114     void *handle;
1115     krb5_principal princ = parse_princ("modify-principal-test");
1116     kadm5_principal_ent_rec ent;
1117     krb5_tl_data tl = { NULL, 1, 1, (uint8_t *)"x" };
1118     krb5_tl_data tl2 = { NULL, 999, 6, (uint8_t *)"foobar" };
1119 
1120     memset(&ent, 0, sizeof(ent));
1121     ent.principal = princ;
1122 
1123     /* Fails with unknown principal. */
1124     mprinc_test_fail("admin", &ent, KADM5_KVNO, KADM5_UNK_PRINC);
1125 
1126     create_simple_princ(princ, NULL);
1127 
1128     /* Fails with prohibited mask bit or tl-data type. */
1129     mprinc_test_fail("admin", &ent, KADM5_AUX_ATTRIBUTES, KADM5_BAD_MASK);
1130     mprinc_test_fail("admin", &ent, KADM5_KEY_DATA, KADM5_BAD_MASK);
1131     mprinc_test_fail("admin", &ent, KADM5_LAST_FAILED, KADM5_BAD_MASK);
1132     mprinc_test_fail("admin", &ent, KADM5_LAST_SUCCESS, KADM5_BAD_MASK);
1133     mprinc_test_fail("admin", &ent, KADM5_LAST_PWD_CHANGE, KADM5_BAD_MASK);
1134     mprinc_test_fail("admin", &ent, KADM5_MKVNO, KADM5_BAD_MASK);
1135     mprinc_test_fail("admin", &ent, KADM5_MOD_NAME, KADM5_BAD_MASK);
1136     mprinc_test_fail("admin", &ent, KADM5_MOD_TIME, KADM5_BAD_MASK);
1137     mprinc_test_fail("admin", &ent, KADM5_PRINCIPAL, KADM5_BAD_MASK);
1138 
1139     /* Fails with tl-data type below 256. */
1140     ent.n_tl_data = 1;
1141     ent.tl_data = &tl;
1142     mprinc_test_fail("admin", &ent, KADM5_TL_DATA, KADM5_BAD_TL_TYPE);
1143 
1144     /* Fails with fail_auth_count other than zero. */
1145     ent.fail_auth_count = 1234;
1146     mprinc_test_fail("admin", &ent, KADM5_FAIL_AUTH_COUNT,
1147                      KADM5_BAD_SERVER_PARAMS);
1148     ent.fail_auth_count = 0;
1149 
1150     /* Succeeds with zero values of various fields. */
1151     mprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION);
1152     mprinc_test_compare("admin", &ent, KADM5_MAX_LIFE);
1153     mprinc_test_compare("admin", &ent, KADM5_MAX_RLIFE);
1154     mprinc_test_compare("admin", &ent, KADM5_FAIL_AUTH_COUNT);
1155     mprinc_test_compare("admin/modify", &ent, KADM5_PRINC_EXPIRE_TIME);
1156     mprinc_test_compare("admin", &ent, KADM5_POLICY_CLR);
1157 
1158     /* Setting a policy causes a pw_expiration computation.  Explicit
1159      * PW_EXPIRATION overrides the policy. */
1160     ent.pw_expiration = 1234;
1161     mprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION);
1162     ent.policy = "dict-only-pol";
1163     mprinc_test_compare("admin", &ent, KADM5_POLICY);
1164     ent.policy = "test-pol";
1165     mprinc_test_compare("admin", &ent, KADM5_POLICY);
1166     ent.pw_expiration = 999999999;
1167     mprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION);
1168     mprinc_test_compare("admin", &ent, KADM5_POLICY_CLR);
1169 
1170     /* Succeeds with non-zero values of various fields. */
1171     ent.princ_expire_time = 1234;
1172     mprinc_test_compare("admin", &ent, KADM5_PRINC_EXPIRE_TIME);
1173     ent.attributes = KRB5_KDB_DISALLOW_ALL_TIX;
1174     mprinc_test_compare("admin", &ent, KADM5_ATTRIBUTES);
1175     ent.attributes = KRB5_KDB_REQUIRES_PWCHANGE;
1176     mprinc_test_compare("admin", &ent, KADM5_ATTRIBUTES);
1177     ent.attributes = KRB5_KDB_DISALLOW_TGT_BASED;
1178     mprinc_test_compare("admin", &ent, KADM5_ATTRIBUTES);
1179     ent.max_life = 3456;
1180     mprinc_test_compare("admin", &ent, KADM5_MAX_LIFE);
1181     ent.kvno = 7;
1182     mprinc_test_compare("admin", &ent, KADM5_KVNO);
1183 
1184     /* Fails over RPC if "modify" ACL is not granted, or if we authenticated to
1185      * kadmin/changepw. */
1186     if (rpc) {
1187         mprinc_test_fail("$admin", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
1188         mprinc_test_fail("admin/none", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
1189         mprinc_test_fail("admin/get", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
1190         mprinc_test_fail("admin/add", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
1191         mprinc_test_fail("admin/delete", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
1192     }
1193 
1194     /* tl-data of type > 255 is accepted. */
1195     handle = get_handle("admin");
1196     ent.max_renewable_life = 88;
1197     ent.tl_data = &tl2;
1198     check(kadm5_modify_principal(handle, &ent,
1199                                  KADM5_MAX_RLIFE | KADM5_TL_DATA));
1200     memset(&ent, 0, sizeof(ent));
1201     check(kadm5_get_principal(handle, princ, &ent,
1202                               KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA));
1203     assert(ent.max_renewable_life == 88);
1204     assert(ent.n_tl_data == 1);
1205     assert(ent.tl_data->tl_data_type == tl2.tl_data_type);
1206     assert(ent.tl_data->tl_data_length == tl2.tl_data_length);
1207     assert(memcmp(ent.tl_data->tl_data_contents, tl2.tl_data_contents,
1208                   tl2.tl_data_length) == 0);
1209     check(kadm5_free_principal_ent(handle, &ent));
1210     free_handle(handle);
1211 
1212     /* Fails with null handle or principal ent. */
1213     mprinc_test_fail(NULL, &ent, KADM5_KVNO, KADM5_BAD_SERVER_HANDLE);
1214     mprinc_test_fail("admin", NULL, KADM5_KVNO, EINVAL);
1215 
1216     delete_princ(princ);
1217     krb5_free_principal(context, princ);
1218 }
1219 
1220 static void
rnd_test_fail(char * user,krb5_principal princ,krb5_error_code code)1221 rnd_test_fail(char *user, krb5_principal princ, krb5_error_code code)
1222 {
1223     void *handle = get_handle(user);
1224 
1225     check_fail(kadm5_randkey_principal(handle, princ, NULL, NULL), code);
1226     free_handle(handle);
1227 }
1228 
1229 static void
rnd_test_succeed(char * user,krb5_principal princ)1230 rnd_test_succeed(char *user, krb5_principal princ)
1231 {
1232     rnd_test_fail(user, princ, 0);
1233 }
1234 
1235 static void
test_randkey()1236 test_randkey()
1237 {
1238     void *handle;
1239     krb5_principal princ = parse_princ("randkey-principal-test");
1240     krb5_principal user_princ = parse_princ("user");
1241     krb5_principal admin_princ = parse_princ("admin");
1242     kadm5_principal_ent_rec ent;
1243     krb5_keyblock *keys;
1244     int n_keys, i;
1245 
1246     create_simple_princ(princ, NULL);
1247 
1248     /* Check kvno and enctypes after randkey. */
1249     handle = get_handle("admin");
1250     check(kadm5_randkey_principal(handle, princ, &keys, &n_keys));
1251     check(kadm5_get_principal(handle, princ, &ent, KADM5_KEY_DATA));
1252     compare_key_data(&ent, default_supported_enctypes);
1253     assert(ent.key_data[0].key_data_kvno == 2);
1254     assert(n_keys == ent.n_key_data);
1255     for (i = 0; i < n_keys; i++)
1256         krb5_free_keyblock_contents(context, &keys[i]);
1257     free(keys);
1258     check(kadm5_free_principal_ent(handle, &ent));
1259     free_handle(handle);
1260 
1261     /*
1262      * Fails over RPC if "change" ACL is not granted, or if we authenticated to
1263      * kadmin/changepw and are changing another principal's password, or for
1264      * self-service if the policy minimum life has not elapsed since the last
1265      * key change.
1266      */
1267     if (rpc) {
1268         rnd_test_fail("$admin", user_princ, KADM5_AUTH_CHANGEPW);
1269         rnd_test_fail("admin/none", user_princ, KADM5_AUTH_CHANGEPW);
1270         rnd_test_fail("admin/delete", user_princ, KADM5_AUTH_CHANGEPW);
1271         rnd_test_succeed("admin/modify", user_princ);
1272         cpw_test_succeed("admin", user_princ, USER_PASSWORD);
1273         rnd_test_fail("user", user_princ, KADM5_PASS_TOOSOON);
1274         rnd_test_fail("$user", user_princ, KADM5_PASS_TOOSOON);
1275     }
1276 
1277     /* Succeeds with change privilege in spite of policy minimum life. */
1278     rnd_test_succeed("admin/modify", user_princ);
1279     cpw_test_succeed("admin", user_princ, USER_PASSWORD);
1280 
1281     /* Succeeds for self-service when authenticating to kadmin/changepw. */
1282     handle = get_handle("$admin");
1283     check(kadm5_randkey_principal(handle, admin_princ, NULL, NULL));
1284     check(kadm5_chpass_principal(handle, admin_princ, ADMIN_PASSWORD));
1285     free_handle(handle);
1286 
1287     /* Fails with null handle or principal name. */
1288     rnd_test_fail(NULL, princ, KADM5_BAD_SERVER_HANDLE);
1289     rnd_test_fail("admin", NULL, EINVAL);
1290 
1291     delete_princ(princ);
1292     krb5_free_principal(context, princ);
1293     krb5_free_principal(context, user_princ);
1294     krb5_free_principal(context, admin_princ);
1295 }
1296 
1297 int
main(int argc,char ** argv)1298 main(int argc, char **argv)
1299 {
1300     assert(argc == 2);
1301     rpc = (strcmp(argv[1], "clnt") == 0);
1302 
1303     check(kadm5_init_krb5_context(&context));
1304 
1305     api = KADM5_API_VERSION_2;
1306     test_create_policy();
1307     test_get_policy();
1308     test_modify_policy();
1309 
1310     api = KADM5_API_VERSION_4;
1311     test_chpass();
1312     test_create_policy();
1313     test_create_principal();
1314     test_delete_policy();
1315     test_delete_principal();
1316     test_get_policy();
1317     test_get_principal();
1318     test_init_destroy();
1319     test_modify_policy();
1320     test_modify_principal();
1321     test_randkey();
1322 
1323     krb5_free_context(context);
1324 
1325     return 0;
1326 }
1327