krb5 commit: Add C test program to replace libkadm5 tcl tests
Greg Hudson
ghudson at mit.edu
Tue May 11 02:08:02 EDT 2021
https://github.com/krb5/krb5/commit/10648ca330e40b07f30165ec51f4a45ef9b96eea
commit 10648ca330e40b07f30165ec51f4a45ef9b96eea
Author: Greg Hudson <ghudson at mit.edu>
Date: Mon Apr 12 01:29:52 2021 -0400
Add C test program to replace libkadm5 tcl tests
src/lib/kadm5/Makefile.in | 9 +
src/lib/kadm5/t_kadm5.c | 1326 +++++++++++++++++++++++++++++++++++++++++++++
src/lib/kadm5/t_kadm5.py | 45 ++
src/util/k5test.py | 2 +-
4 files changed, 1381 insertions(+), 1 deletions(-)
diff --git a/src/lib/kadm5/Makefile.in b/src/lib/kadm5/Makefile.in
index c4eaad3..f94c0a7 100644
--- a/src/lib/kadm5/Makefile.in
+++ b/src/lib/kadm5/Makefile.in
@@ -84,6 +84,15 @@ all-prerecurse: all-libobjs
all-windows: $(OBJS)
+t_kadm5clnt: t_kadm5.o $(KADMCLNT_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ t_kadm5.o $(KADMCLNT_LIBS) $(KRB5_BASE_LIBS)
+
+t_kadm5srv: t_kadm5.o $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ t_kadm5.o $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
+
+check-pytests: t_kadm5clnt t_kadm5srv
+ $(RUNPYTEST) $(srcdir)/t_kadm5.py $(PYTESTFLAGS)
+
generate-files-mac-prerecurse: includes
check-windows:
diff --git a/src/lib/kadm5/t_kadm5.c b/src/lib/kadm5/t_kadm5.c
new file mode 100644
index 0000000..153147f
--- /dev/null
+++ b/src/lib/kadm5/t_kadm5.c
@@ -0,0 +1,1326 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/kadm5/t_kadm5.c - API tests for libkadm5 */
+/*
+ * Copyright (C) 2021 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include <kadm5/admin.h>
+
+static uint32_t api;
+static krb5_boolean rpc;
+
+static krb5_context context;
+
+/* These must match the creation commands in t_kadm5.py. */
+#define ADMIN_PASSWORD "admin"
+#define USER_PASSWORD "us3r"
+
+/* This list must match the supported_enctypes setting in t_kadm5.py. */
+static krb5_enctype
+default_supported_enctypes[] = {
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ ENCTYPE_NULL
+};
+
+static void
+check(krb5_error_code code)
+{
+ assert(code == 0);
+}
+
+static void
+check_fail(krb5_error_code code, krb5_error_code expected)
+{
+ assert(code == expected);
+}
+
+/*
+ * Initialize a handle using the global context. The caller must destroy this
+ * handle before initializing another one. If the client name begins with '$',
+ * authenticate to kadmin/changepw; otherwise authenticate to kadmin/admin. If
+ * client is null, return a null handle.
+ */
+static void *
+get_handle(char *client)
+{
+ void *handle;
+ char *service, *pass;
+
+ if (client == NULL)
+ return NULL;
+
+ if (*client == '$') {
+ service = KADM5_CHANGEPW_SERVICE;
+ client++;
+ } else {
+ service = KADM5_ADMIN_SERVICE;
+ }
+ pass = (strcmp(client, "user") == 0) ? USER_PASSWORD : ADMIN_PASSWORD;
+
+ check(kadm5_init(context, client, pass, service, NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle));
+ return handle;
+}
+
+static void
+free_handle(void *handle)
+{
+ if (handle != NULL)
+ check(kadm5_destroy(handle));
+}
+
+static krb5_principal
+parse_princ(const char *str)
+{
+ krb5_principal princ;
+
+ check(krb5_parse_name(context, str, &princ));
+ return princ;
+}
+
+static void
+create_simple_policy(char *name)
+{
+ void *handle = get_handle("admin");
+ kadm5_policy_ent_rec ent;
+
+ memset(&ent, 0, sizeof(ent));
+ ent.policy = name;
+ check(kadm5_create_policy(handle, &ent, KADM5_POLICY));
+ free_handle(handle);
+}
+
+static void
+delete_policy(char *name)
+{
+ void *handle = get_handle("admin");
+
+ check(kadm5_delete_policy(handle, name));
+ free_handle(handle);
+}
+
+static void
+compare_policy(kadm5_policy_ent_t x, uint32_t mask)
+{
+ kadm5_policy_ent_rec g;
+ void *handle = get_handle("admin");
+
+ check(kadm5_get_policy(handle, x->policy, &g));
+
+ assert(strcmp(g.policy, x->policy) == 0);
+ if (mask & KADM5_PW_MAX_LIFE)
+ assert(g.pw_max_life == x->pw_max_life);
+ if (mask & KADM5_PW_MIN_LIFE)
+ assert(g.pw_min_life == x->pw_min_life);
+ if (mask & KADM5_PW_MIN_LENGTH)
+ assert(g.pw_min_length == x->pw_min_length);
+ if (mask & KADM5_PW_MIN_CLASSES)
+ assert(g.pw_min_classes == x->pw_min_classes);
+ if (mask & KADM5_PW_HISTORY_NUM)
+ assert(g.pw_history_num == x->pw_history_num);
+ if (mask & KADM5_PW_MAX_FAILURE)
+ assert(g.pw_max_fail == x->pw_max_fail);
+ if (mask & KADM5_PW_FAILURE_COUNT_INTERVAL)
+ assert(g.pw_failcnt_interval == x->pw_failcnt_interval);
+ if (mask & KADM5_PW_LOCKOUT_DURATION)
+ assert(g.pw_lockout_duration == x->pw_lockout_duration);
+
+ check(kadm5_free_policy_ent(handle, &g));
+ free_handle(handle);
+}
+
+static void
+create_simple_princ(krb5_principal princ, char *policy)
+{
+ void *handle = get_handle("admin");
+ kadm5_principal_ent_rec ent;
+ uint32_t mask = KADM5_PRINCIPAL;
+
+ memset(&ent, 0, sizeof(ent));
+ ent.principal = princ;
+ ent.policy = policy;
+ if (policy != NULL)
+ mask |= KADM5_POLICY;
+ check(kadm5_create_principal(handle, &ent, mask, "pw"));
+ free_handle(handle);
+}
+
+static void
+delete_princ(krb5_principal princ)
+{
+ void *handle = get_handle("admin");
+
+ check(kadm5_delete_principal(handle, princ));
+ free_handle(handle);
+}
+
+static void
+compare_key_data(kadm5_principal_ent_t ent, const krb5_enctype *etypes)
+{
+ int i;
+
+ for (i = 0; etypes[i] != ENCTYPE_NULL; i++) {
+ assert(i < ent->n_key_data);
+ assert(ent->key_data[i].key_data_ver >= 1);
+ assert(ent->key_data[i].key_data_type[0] == etypes[i]);
+ }
+}
+
+static void
+compare_princ(kadm5_principal_ent_t x, uint32_t mask)
+{
+ void *handle = get_handle("admin");
+ kadm5_principal_ent_rec g;
+ kadm5_policy_ent_rec pol;
+
+ check(kadm5_get_principal(handle, x->principal, &g,
+ KADM5_PRINCIPAL_NORMAL_MASK));
+
+ assert(krb5_principal_compare(context, g.principal, x->principal));
+ if (mask & KADM5_POLICY)
+ assert(strcmp(g.policy, x->policy) == 0);
+ if (mask & KADM5_PRINC_EXPIRE_TIME)
+ assert(g.princ_expire_time == x->princ_expire_time);
+ if (mask & KADM5_MAX_LIFE)
+ assert(g.max_life == x->max_life);
+ if (mask & KADM5_MAX_RLIFE)
+ assert(g.max_renewable_life == x->max_renewable_life);
+ if (mask & KADM5_FAIL_AUTH_COUNT)
+ assert(g.fail_auth_count == x->fail_auth_count);
+ if (mask & KADM5_ATTRIBUTES)
+ assert(g.attributes == x->attributes);
+ if (mask & KADM5_KVNO)
+ assert(g.kvno == x->kvno);
+
+ if (mask & KADM5_PW_EXPIRATION) {
+ assert(g.pw_expiration == x->pw_expiration);
+ } else if ((mask & KADM5_POLICY) &&
+ kadm5_get_policy(handle, g.policy, &pol) == 0) {
+ /* Check the policy pw_max_life computation. */
+ if (pol.pw_max_life != 0) {
+ assert(ts_incr(g.last_pwd_change, pol.pw_max_life) ==
+ g.pw_expiration);
+ } else {
+ assert(g.pw_expiration == 0);
+ }
+ check(kadm5_free_policy_ent(handle, &pol));
+ }
+
+ if (mask & KADM5_POLICY_CLR) {
+ assert(g.policy == NULL);
+ if (!(mask & KADM5_PW_EXPIRATION))
+ assert(g.pw_expiration == 0);
+ }
+
+ check(kadm5_free_principal_ent(handle, &g));
+ free_handle(handle);
+}
+
+static void
+kinit(krb5_ccache cc, const char *user, const char *pass, const char *service)
+{
+ krb5_get_init_creds_opt *opt;
+ krb5_principal client = parse_princ(user);
+ krb5_creds creds;
+
+ check(krb5_get_init_creds_opt_alloc(context, &opt));
+ check(krb5_get_init_creds_opt_set_out_ccache(context, opt, cc));
+ check(krb5_get_init_creds_password(context, &creds, client, pass, NULL,
+ NULL, 0, service, opt));
+ krb5_get_init_creds_opt_free(context, opt);
+ krb5_free_cred_contents(context, &creds);
+ krb5_free_principal(context, client);
+}
+
+static void
+cpw_test_fail(char *user, krb5_principal princ, char *pass,
+ krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_chpass_principal(handle, princ, pass), code);
+ free_handle(handle);
+}
+
+static void
+cpw_test_succeed(char *user, krb5_principal princ, char *pass)
+{
+ cpw_test_fail(user, princ, pass, 0);
+}
+
+static void
+test_chpass()
+{
+ krb5_principal princ = parse_princ("chpass-test");
+ krb5_principal hist_princ = parse_princ("kadmin/history");
+ kadm5_principal_ent_rec ent;
+ void *handle;
+
+ /* Specify a policy so that kadmin/history is created. */
+ create_simple_princ(princ, "minlife-pol");
+
+ /* Check kvno and enctypes after a password change. */
+ handle = get_handle("admin");
+ check(kadm5_chpass_principal(handle, princ, "newpassword"));
+ check(kadm5_get_principal(handle, princ, &ent, KADM5_KEY_DATA));
+ compare_key_data(&ent, default_supported_enctypes);
+ assert(ent.key_data[0].key_data_kvno == 2);
+ check(kadm5_free_principal_ent(handle, &ent));
+ free_handle(handle);
+
+ /* Fails for protected principal. */
+ cpw_test_fail("admin", hist_princ, "pw", KADM5_PROTECT_PRINCIPAL);
+
+ /* Fails over RPC if "change" ACL is not granted, or if we authenticated to
+ * kadmin/changepw and are changing another principal's password. */
+ if (rpc) {
+ cpw_test_succeed("admin/modify", princ, "pw2");
+ cpw_test_fail("admin/none", princ, "pw3", KADM5_AUTH_CHANGEPW);
+ cpw_test_fail("$admin", princ, "pw3", KADM5_AUTH_CHANGEPW);
+ }
+
+ /* Fails with null handle or principal name. */
+ cpw_test_fail(NULL, princ, "pw", KADM5_BAD_SERVER_HANDLE);
+ cpw_test_fail("admin", NULL, "pw", EINVAL);
+
+ delete_princ(princ);
+ krb5_free_principal(context, princ);
+ krb5_free_principal(context, hist_princ);
+}
+
+static void
+cpol_test_fail(char *user, kadm5_policy_ent_t ent, uint32_t mask,
+ krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_create_policy(handle, ent, mask | KADM5_POLICY), code);
+ free_handle(handle);
+}
+
+static void
+cpol_test_compare(char *user, kadm5_policy_ent_t ent, uint32_t mask)
+{
+ cpol_test_fail(user, ent, mask, 0);
+ compare_policy(ent, mask);
+ delete_policy(ent->policy);
+}
+
+static void
+test_create_policy()
+{
+ void *handle;
+ kadm5_policy_ent_rec ent;
+
+ memset(&ent, 0, sizeof(ent));
+
+ /* Fails with undefined mask bit. */
+ ent.policy = "create-policy-test";
+ cpol_test_fail("admin", &ent, 0x10000000, KADM5_BAD_MASK);
+
+ /* Fails without KADM5_POLICY mask bit. */
+ handle = get_handle("admin");
+ check_fail(kadm5_create_policy(handle, &ent, 0), KADM5_BAD_MASK);
+ free_handle(handle);
+
+ /* pw_min_life = 0 and pw_min_life != 0 */
+ cpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
+ ent.pw_min_life = 32;
+ cpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
+
+ /* pw_max_life = 0 and pw_max_life != 0 */
+ cpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
+ ent.pw_max_life = 32;
+ cpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
+
+ /* pw_min_length = 0 (rejected) and pw_min_length != 0 */
+ cpol_test_fail("admin", &ent, KADM5_PW_MIN_LENGTH, KADM5_BAD_LENGTH);
+ ent.pw_min_length = 32;
+ cpol_test_compare("admin", &ent, KADM5_PW_MIN_LENGTH);
+
+ /* pw_min_classes = 0 (rejected), 1, 5, 6 (rejected) */
+ cpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
+ ent.pw_min_classes = 1;
+ cpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
+ ent.pw_min_classes = 5;
+ cpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
+ ent.pw_min_classes = 6;
+ cpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
+
+ /* pw_history_num = 0 (rejected), 1, 10 */
+ cpol_test_fail("admin", &ent, KADM5_PW_HISTORY_NUM, KADM5_BAD_HISTORY);
+ ent.pw_history_num = 1;
+ cpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
+ ent.pw_history_num = 10;
+ cpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
+
+ if (api >= KADM5_API_VERSION_3) {
+ ent.pw_max_fail = 2;
+ cpol_test_compare("admin", &ent, KADM5_PW_MAX_FAILURE);
+ ent.pw_failcnt_interval = 90;
+ cpol_test_compare("admin", &ent,
+ KADM5_PW_FAILURE_COUNT_INTERVAL);
+ ent.pw_lockout_duration = 180;
+ cpol_test_compare("admin", &ent, KADM5_PW_LOCKOUT_DURATION);
+ }
+
+ /* Fails over RPC if "add" ACL is not granted, or if we authenticated to
+ * kadmin/changepw. */
+ if (rpc) {
+ cpol_test_fail("$admin", &ent, 0, KADM5_AUTH_ADD);
+ cpol_test_fail("admin/none", &ent, 0, KADM5_AUTH_ADD);
+ cpol_test_fail("admin/get", &ent, 0, KADM5_AUTH_ADD);
+ cpol_test_fail("admin/modify", &ent, 0, KADM5_AUTH_ADD);
+ cpol_test_fail("admin/delete", &ent, 0, KADM5_AUTH_ADD);
+ cpol_test_compare("admin/add", &ent, 0);
+ }
+
+ /* Fails with existing policy name. */
+ ent.policy = "test-pol";
+ cpol_test_fail("admin", &ent, 0, KADM5_DUP);
+
+ /* Fails with null or empty policy name, or invalid character in name. */
+ ent.policy = NULL;
+ cpol_test_fail("admin", &ent, 0, EINVAL);
+ ent.policy = "";
+ cpol_test_fail("admin", &ent, 0, KADM5_BAD_POLICY);
+ ent.policy = "pol\7";
+ cpol_test_fail("admin", &ent, 0, KADM5_BAD_POLICY);
+
+ /* Fails with null handle or policy ent. */
+ cpol_test_fail(NULL, &ent, 0, KADM5_BAD_SERVER_HANDLE);
+ cpol_test_fail("admin", NULL, 0, EINVAL);
+}
+
+static void
+cprinc_test_fail(char *user, kadm5_principal_ent_t ent, uint32_t mask,
+ char *pass, krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_create_principal(handle, ent, mask | KADM5_PRINCIPAL,
+ pass), code);
+ free_handle(handle);
+}
+
+static void
+cprinc_test_compare(char *user, kadm5_principal_ent_t ent, uint32_t mask,
+ char *pass)
+{
+ cprinc_test_fail(user, ent, mask, pass, 0);
+ compare_princ(ent, mask);
+ delete_princ(ent->principal);
+}
+
+static void
+test_create_principal()
+{
+ void *handle;
+ kadm5_principal_ent_rec ent;
+ krb5_principal princ = parse_princ("create-principal-test");
+ krb5_principal user_princ = parse_princ("user");
+
+ memset(&ent, 0, sizeof(ent));
+ ent.principal = princ;
+
+ /* Fails with undefined or prohibited mask bit. */
+ cprinc_test_fail("admin", &ent, 0x100000, "", KADM5_BAD_MASK);
+ cprinc_test_fail("admin", &ent, KADM5_LAST_PWD_CHANGE, "pw",
+ KADM5_BAD_MASK);
+ cprinc_test_fail("admin", &ent, KADM5_MOD_TIME, "pw", KADM5_BAD_MASK);
+ cprinc_test_fail("admin", &ent, KADM5_MOD_NAME, "pw", KADM5_BAD_MASK);
+ cprinc_test_fail("admin", &ent, KADM5_MKVNO, "pw", KADM5_BAD_MASK);
+ cprinc_test_fail("admin", &ent, KADM5_AUX_ATTRIBUTES, "pw",
+ KADM5_BAD_MASK);
+
+ /* Fails without KADM5_PRINCIPAL mask bit. */
+ handle = get_handle("admin");
+ check_fail(kadm5_create_principal(handle, &ent, 0, "pw"), KADM5_BAD_MASK);
+ free_handle(handle);
+
+ /* Fails with empty password or password prohibited by policy. */
+ cprinc_test_fail("admin", &ent, 0, "", KADM5_PASS_Q_TOOSHORT);
+ ent.policy = "test-pol";
+ cprinc_test_fail("admin", &ent, KADM5_POLICY, "tP", KADM5_PASS_Q_TOOSHORT);
+ cprinc_test_fail("admin", &ent, KADM5_POLICY, "testpassword",
+ KADM5_PASS_Q_CLASS);
+ cprinc_test_fail("admin", &ent, KADM5_POLICY, "Abyssinia",
+ KADM5_PASS_Q_DICT);
+
+ cprinc_test_compare("admin", &ent, 0, "pw");
+ ent.policy = "nonexistent-pol";
+ cprinc_test_compare("admin", &ent, KADM5_POLICY, "pw");
+ cprinc_test_compare("admin/rename", &ent, KADM5_POLICY, "pw");
+
+ /* Test pw_expiration explicit specifications vs. policy pw_max_life. */
+ ent.policy = "test-pol";
+ cprinc_test_compare("admin", &ent, KADM5_POLICY, "NotinTheDictionary");
+ cprinc_test_compare("admin", &ent, KADM5_PRINC_EXPIRE_TIME, "pw");
+ cprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION, "pw");
+ cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
+ "NotinTheDictionary");
+ ent.pw_expiration = 1234;
+ cprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION, "pw");
+ cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
+ "NotinTheDictionary");
+ ent.pw_expiration = 999999999;
+ cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
+ "NotinTheDictionary");
+ ent.policy = "dict-only-pol";
+ cprinc_test_compare("admin", &ent, KADM5_POLICY | KADM5_PW_EXPIRATION,
+ "pw");
+
+ /* Fails over RPC if "add" ACL is not granted, or if we authenticated to
+ * kadmin/changepw. */
+ if (rpc) {
+ cprinc_test_fail("$admin", &ent, 0, "pw", KADM5_AUTH_ADD);
+ cprinc_test_fail("admin/none", &ent, 0, "pw", KADM5_AUTH_ADD);
+ cprinc_test_fail("admin/get", &ent, 0, "pw", KADM5_AUTH_ADD);
+ cprinc_test_fail("admin/modify", &ent, 0, "pw", KADM5_AUTH_ADD);
+ cprinc_test_fail("admin/delete", &ent, 0, "pw", KADM5_AUTH_ADD);
+ }
+
+ /* Fails with existing policy name. */
+ ent.principal = user_princ;
+ cprinc_test_fail("admin", &ent, 0, "pw", KADM5_DUP);
+
+ /* Fails with null handle or principal ent. */
+ cprinc_test_fail(NULL, &ent, 0, "pw", KADM5_BAD_SERVER_HANDLE);
+ cprinc_test_fail("admin", NULL, 0, "pw", EINVAL);
+
+ krb5_free_principal(context, princ);
+ krb5_free_principal(context, user_princ);
+}
+
+static void
+dpol_test_fail(char *user, char *name, krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_delete_policy(handle, name), code);
+ free_handle(handle);
+}
+
+static void
+dpol_test_succeed(char *user, char *name)
+{
+ dpol_test_fail(user, name, 0);
+}
+
+static void
+test_delete_policy()
+{
+ krb5_principal princ = parse_princ("delete-policy-test-princ");
+
+ /* Fails with unknown policy. */
+ dpol_test_fail("admin", "delete-policy-test", KADM5_UNK_POLICY);
+
+ /* Fails with empty policy name. */
+ dpol_test_fail("admin", "", KADM5_BAD_POLICY);
+
+ /* Succeeds with "delete" ACL (or local authentication). */
+ create_simple_policy("delete-policy-test");
+ dpol_test_succeed("admin/delete", "delete-policy-test");
+
+ /* Succeeds even if a principal references the policy, since we now allow
+ * principals to reference nonexistent policies. */
+ create_simple_policy("delete-policy-test");
+ create_simple_princ(princ, "delete-policy-test");
+ dpol_test_succeed("admin", "delete-policy-test");
+ delete_princ(princ);
+
+ /* Fails over RPC if "delete" ACL is not granted, or if we authenticated to
+ * kadmin/changepw. */
+ if (rpc) {
+ dpol_test_fail("$admin", "test-pol", KADM5_AUTH_DELETE);
+ dpol_test_fail("admin/none", "test-pol", KADM5_AUTH_DELETE);
+ dpol_test_fail("admin/add", "test-pol", KADM5_AUTH_DELETE);
+ }
+
+ /* Fails with null handle or principal ent. */
+ dpol_test_fail(NULL, "test-pol", KADM5_BAD_SERVER_HANDLE);
+ dpol_test_fail("admin", NULL, EINVAL);
+
+ krb5_free_principal(context, princ);
+}
+
+static void
+dprinc_test_fail(char *user, krb5_principal princ, krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_delete_principal(handle, princ), code);
+ free_handle(handle);
+}
+
+static void
+dprinc_test_succeed(char *user, krb5_principal princ)
+{
+ dprinc_test_fail(user, princ, 0);
+}
+
+static void
+test_delete_principal()
+{
+ krb5_principal princ = parse_princ("delete-principal-test");
+
+ /* Fails with unknown principal. */
+ dprinc_test_fail("admin", princ, KADM5_UNK_PRINC);
+
+ /* Succeeds with "delete" ACL (or local authentication). */
+ create_simple_princ(princ, NULL);
+ dprinc_test_succeed("admin/delete", princ);
+
+ /* Fails over RPC if "delete" ACL is not granted, or if we authenticated to
+ * kadmin/changepw. */
+ if (rpc) {
+ dprinc_test_fail("$admin", princ, KADM5_AUTH_DELETE);
+ dprinc_test_fail("admin/add", princ, KADM5_AUTH_DELETE);
+ dprinc_test_fail("admin/modify", princ, KADM5_AUTH_DELETE);
+ dprinc_test_fail("admin/get", princ, KADM5_AUTH_DELETE);
+ dprinc_test_fail("admin/none", princ, KADM5_AUTH_DELETE);
+ }
+
+ /* Fails with null handle or principal ent. */
+ dprinc_test_fail(NULL, princ, KADM5_BAD_SERVER_HANDLE);
+ dprinc_test_fail("admin", NULL, EINVAL);
+
+ krb5_free_principal(context, princ);
+}
+
+static void
+gpol_test_succeed(char *user, char *name)
+{
+ void *handle = get_handle(user);
+ kadm5_policy_ent_rec ent;
+
+ check(kadm5_get_policy(handle, name, &ent));
+ assert(strcmp(ent.policy, name) == 0);
+ check(kadm5_free_policy_ent(handle, &ent));
+ free_handle(handle);
+}
+
+static void
+gpol_test_fail(char *user, char *name, krb5_error_code code)
+{
+ void *handle = get_handle(user);
+ kadm5_policy_ent_rec ent;
+
+ check_fail(kadm5_get_policy(handle, name, &ent), code);
+ free_handle(handle);
+}
+
+static void
+test_get_policy()
+{
+ /* Fails with unknown policy. */
+ dpol_test_fail("admin", "unknown-policy", KADM5_UNK_POLICY);
+
+ /* Fails with empty or null policy name or a null handle. */
+ gpol_test_fail("admin", "", KADM5_BAD_POLICY);
+ gpol_test_fail("admin", NULL, EINVAL);
+ gpol_test_fail(NULL, "", KADM5_BAD_SERVER_HANDLE);
+
+ /* Fails over RPC unless "get" ACL is granted or the principal's own policy
+ * is retrieved. */
+ if (rpc) {
+ gpol_test_fail("admin/none", "test-pol", KADM5_AUTH_GET);
+ gpol_test_fail("admin/add", "test-pol", KADM5_AUTH_GET);
+ gpol_test_succeed("admin/get", "test-pol");
+ gpol_test_succeed("user", "minlife-pol");
+ gpol_test_succeed("$user", "minlife-pol");
+ }
+}
+
+static void
+gprinc_test_succeed(char *user, krb5_principal princ)
+{
+ void *handle = get_handle(user);
+ kadm5_principal_ent_rec ent;
+
+ check(kadm5_get_principal(handle, princ, &ent,
+ KADM5_PRINCIPAL_NORMAL_MASK));
+ assert(krb5_principal_compare(context, ent.principal, princ));
+ check(kadm5_free_principal_ent(handle, &ent));
+ free_handle(handle);
+}
+
+static void
+gprinc_test_fail(char *user, krb5_principal princ, krb5_error_code code)
+{
+ void *handle = get_handle(user);
+ kadm5_principal_ent_rec ent;
+
+ check_fail(kadm5_get_principal(handle, princ, &ent,
+ KADM5_PRINCIPAL_NORMAL_MASK), code);
+ free_handle(handle);
+}
+
+static void
+test_get_principal()
+{
+ void *handle;
+ kadm5_principal_ent_rec ent;
+ krb5_principal princ = parse_princ("get-principal-test");
+ krb5_principal admin_princ = parse_princ("admin");
+ krb5_principal admin_none_princ = parse_princ("admin/none");
+ int i;
+
+ /* Fails with unknown principal. */
+ gprinc_test_fail("admin", princ, KADM5_UNK_PRINC);
+
+ create_simple_princ(princ, NULL);
+
+ /* Succeeds with "get" ACL (or local authentication), or operating on
+ * self. */
+ gprinc_test_succeed("admin/none", admin_none_princ);
+ gprinc_test_succeed("$admin", admin_princ);
+ gprinc_test_succeed("admin/get", princ);
+
+ /* Fails over RPC if "get" ACL is not granted, or if we authenticated to
+ * kadmin/changepw and getting another principal entry. */
+ if (rpc) {
+ gprinc_test_fail("$admin", princ, KADM5_AUTH_GET);
+ gprinc_test_fail("admin/none", princ, KADM5_AUTH_GET);
+ gprinc_test_fail("admin/add", princ, KADM5_AUTH_GET);
+ gprinc_test_fail("admin/modify", princ, KADM5_AUTH_GET);
+ gprinc_test_fail("admin/delete", princ, KADM5_AUTH_GET);
+ }
+
+ /* Entry contains no key data or tl-data unless asked for. */
+ handle = get_handle("admin");
+ check(kadm5_get_principal(handle, princ, &ent,
+ KADM5_PRINCIPAL_NORMAL_MASK));
+ assert(ent.n_tl_data == 0);
+ assert(ent.n_key_data == 0);
+ assert(ent.tl_data == NULL);
+ check(kadm5_free_principal_ent(handle, &ent));
+
+ /* Key data (without the actual keys over RPC) is provided if asked for. */
+ check(kadm5_get_principal(handle, princ, &ent,
+ KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA));
+ assert(ent.n_key_data == 2);
+ for (i = 0; i < ent.n_key_data; i++)
+ assert(rpc == (ent.key_data[i].key_data_length[0] == 0));
+ check(kadm5_free_principal_ent(handle, &ent));
+ free_handle(handle);
+
+ /* Fails with null handle or principal. */
+ gprinc_test_fail(NULL, princ, KADM5_BAD_SERVER_HANDLE);
+ gprinc_test_fail("admin", NULL, EINVAL);
+
+ delete_princ(princ);
+ krb5_free_principal(context, princ);
+ krb5_free_principal(context, admin_princ);
+ krb5_free_principal(context, admin_none_princ);
+}
+
+static void
+test_init_destroy()
+{
+ krb5_context ctx;
+ kadm5_ret_t ret;
+ kadm5_config_params params;
+ kadm5_principal_ent_rec ent, gent;
+ krb5_principal princ = parse_princ("init-test");
+ krb5_ccache cc;
+ void *handle;
+ char hostname[MAXHOSTNAMELEN];
+ int r;
+
+ memset(¶ms, 0, sizeof(params));
+ memset(&ent, 0, sizeof(ent));
+ ent.principal = princ;
+
+ r = gethostname(hostname, sizeof(hostname));
+ assert(r == 0);
+
+ /* Destroy fails with no server handle. */
+ check_fail(kadm5_destroy(NULL), KADM5_BAD_SERVER_HANDLE);
+
+ /* Fails with bad structure version mask. */
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ 0x65432101, api, NULL, &handle),
+ KADM5_BAD_STRUCT_VERSION);
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ 1, api, NULL, &handle), KADM5_BAD_STRUCT_VERSION);
+
+ /* Fails with too-old or too-new structure version. */
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION_MASK, api, NULL, &handle),
+ KADM5_OLD_STRUCT_VERSION);
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION_MASK | 0xca, api, NULL,
+ &handle), KADM5_NEW_STRUCT_VERSION);
+
+ /* Fails with bad API version mask. */
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, 0x65432100, NULL, &handle),
+ KADM5_BAD_API_VERSION);
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, 4, NULL, &handle),
+ KADM5_BAD_API_VERSION);
+
+ /* Fails with too-old or too-new API version.*/
+ ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_MASK, NULL,
+ &handle);
+ assert(ret == (rpc ? KADM5_OLD_LIB_API_VERSION :
+ KADM5_OLD_SERVER_API_VERSION));
+ ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_MASK | 0xca, NULL,
+ &handle);
+ assert(ret == (rpc ? KADM5_NEW_LIB_API_VERSION :
+ KADM5_NEW_SERVER_API_VERSION));
+
+ /* Fails with structure and API version reversed. */
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
+ api, KADM5_STRUCT_VERSION, NULL, &handle),
+ KADM5_BAD_STRUCT_VERSION);
+
+ /* Hardcoded default max lifetime is used when no handle or krb5.conf
+ * setting is given. */
+ handle = get_handle("admin");
+ check(kadm5_create_principal(handle, &ent, KADM5_PRINCIPAL, "pw"));
+ check(kadm5_get_principal(handle, princ, &gent,
+ KADM5_PRINCIPAL_NORMAL_MASK));
+ assert(gent.max_life == KRB5_KDB_MAX_LIFE);
+ check(kadm5_delete_principal(handle, princ));
+ check(kadm5_free_principal_ent(handle, &gent));
+ free_handle(handle);
+
+ /* Fails with configured unknown realm. Do these tests in separate krb5
+ * contexts since the realm setting sticks to the context. */
+ check(kadm5_init_krb5_context(&ctx));
+ params.realm = "";
+ params.mask = KADM5_CONFIG_REALM;
+ ret = kadm5_init(ctx, "admin", "admin", KADM5_ADMIN_SERVICE, ¶ms,
+ KADM5_STRUCT_VERSION, api, NULL, &handle);
+ assert(ret == (rpc ? KADM5_MISSING_KRB5_CONF_PARAMS : ENOENT));
+ krb5_free_context(ctx);
+
+ check(kadm5_init_krb5_context(&ctx));
+ params.realm = "@";
+ ret = kadm5_init(ctx, "admin", "admin", KADM5_ADMIN_SERVICE, ¶ms,
+ KADM5_STRUCT_VERSION, api, NULL, &handle);
+ assert(ret == (rpc ? KADM5_MISSING_KRB5_CONF_PARAMS : ENOENT));
+ krb5_free_context(ctx);
+
+ check(kadm5_init_krb5_context(&ctx));
+ params.realm = "BAD.REALM";
+ ret = kadm5_init(ctx, "admin", "admin", KADM5_ADMIN_SERVICE, ¶ms,
+ KADM5_STRUCT_VERSION, api, NULL, &handle);
+ assert(ret == (rpc ? KADM5_MISSING_KRB5_CONF_PARAMS : ENOENT));
+ krb5_free_context(ctx);
+
+ /* Succeeds with explicit client realm and configured realm. */
+ check(kadm5_init_krb5_context(&ctx));
+ params.realm = "KRBTEST.COM";
+ check(kadm5_init(ctx, "admin at KRBTEST.COM", "admin", KADM5_ADMIN_SERVICE,
+ ¶ms, KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_destroy(handle));
+ krb5_free_context(ctx);
+
+ /* Succeeds with explicit client realm. */
+ check(kadm5_init(context, "admin at KRBTEST.COM", "admin",
+ KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION, api,
+ NULL, &handle));
+ check(kadm5_destroy(handle));
+
+
+ if (rpc) {
+ check(krb5_cc_default(context, &cc));
+
+ /* Succeeds with configured host and port. */
+ params.admin_server = hostname;
+ params.kadmind_port = 61001;
+ params.mask = KADM5_CONFIG_ADMIN_SERVER | KADM5_CONFIG_KADMIND_PORT;
+ check(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
+ ¶ms, KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_destroy(handle));
+
+ /* Fails with wrong configured port. */
+ params.kadmind_port = 4;
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
+ ¶ms, KADM5_STRUCT_VERSION, api, NULL,
+ &handle), KADM5_RPC_ERROR);
+
+ /* Fails with non-resolving hostname. */
+ params.admin_server = "does.not.exist";
+ params.mask = KADM5_CONFIG_ADMIN_SERVER;
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
+ ¶ms, KADM5_STRUCT_VERSION, api, NULL,
+ &handle), KADM5_CANT_RESOLVE);
+
+ /* Fails with uninitialized cache. */
+ check_fail(kadm5_init_with_creds(context, "admin", cc,
+ KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, api, NULL,
+ &handle), KRB5_FCC_NOFILE);
+
+ /* Succeeds with cache containing kadmin/admin cred. */
+ kinit(cc, "admin", "admin", KADM5_ADMIN_SERVICE);
+ check(kadm5_init_with_creds(context, "admin", cc, KADM5_ADMIN_SERVICE,
+ NULL, KADM5_STRUCT_VERSION, api, NULL,
+ &handle));
+ check(kadm5_destroy(handle));
+
+ /* Succeeds with cache containing kadmin/changepw cred. */
+ kinit(cc, "admin", "admin", KADM5_CHANGEPW_SERVICE);
+ check(kadm5_init_with_creds(context, "admin", cc,
+ KADM5_CHANGEPW_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_destroy(handle));
+
+ /* Fails with cache containing only a TGT. */
+ kinit(cc, "admin", "admin", NULL);
+ check_fail(kadm5_init_with_creds(context, "admin", cc,
+ KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, api, NULL,
+ &handle), KRB5_CC_NOTFOUND);
+
+ /* Fails authenticating to non-kadmin princ. */
+ check_fail(kadm5_init(context, "admin", "admin", "user", NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle),
+ KADM5_RPC_ERROR);
+
+ /* Fails authenticating to nonexistent princ. */
+ check_fail(kadm5_init(context, "admin", "admin", "noexist", NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle),
+ KADM5_SECURE_PRINC_MISSING);
+
+ /* Fails authenticating to client princ (which is non-kadmin). */
+ check_fail(kadm5_init(context, "admin", "admin", "admin", NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle),
+ KADM5_RPC_ERROR);
+
+ /* Fails with wrong password. */
+ check_fail(kadm5_init(context, "admin", "wrong", KADM5_ADMIN_SERVICE,
+ NULL, KADM5_STRUCT_VERSION, api, NULL, &handle),
+ KADM5_BAD_PASSWORD);
+
+ /* Fails with null client name. */
+ check_fail(kadm5_init(context, NULL, "admin", KADM5_ADMIN_SERVICE,
+ NULL, KADM5_STRUCT_VERSION, api, NULL, &handle),
+ EINVAL);
+
+ /* Fails with nonexistent client name. */
+ check_fail(kadm5_init(context, "noexist", "admin", KADM5_ADMIN_SERVICE,
+ NULL, KADM5_STRUCT_VERSION, api, NULL, &handle),
+ KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN);
+
+ /* Fails with nonexistent client name with explicit realm. */
+ check_fail(kadm5_init(context, "noexist at KRBTEST.COM", "admin",
+ KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION,
+ api, NULL, &handle),
+ KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN);
+
+ /* Fails with nonexistent client name with unknown realm. */
+ check_fail(kadm5_init(context, "noexist at BAD.REALM", "admin",
+ KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION,
+ api, NULL, &handle), KRB5_REALM_UNKNOWN);
+
+ /* Fails with known name but unknown realm. */
+ check_fail(kadm5_init(context, "admin at BAD.REALM", "admin",
+ KADM5_ADMIN_SERVICE, NULL, KADM5_STRUCT_VERSION,
+ api, NULL, &handle), KRB5_REALM_UNKNOWN);
+
+ check(krb5_cc_destroy(context, cc));
+ } else {
+ /* Fails with nonexistent stash file. */
+ params.stash_file = "does/not/exist";
+ params.mask = KADM5_CONFIG_STASH_FILE;
+ check_fail(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
+ ¶ms, KADM5_STRUCT_VERSION, api, NULL,
+ &handle), KRB5_KDB_CANTREAD_STORED);
+
+ /* Uses configured defaults for principal creation. */
+ params.max_life = 10;
+ params.max_rlife = 20;
+ params.expiration = 30;
+ params.num_keysalts = 0;
+ params.mask = KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE |
+ KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES;
+ check(kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE,
+ ¶ms, KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_create_principal(handle, &ent, KADM5_PRINCIPAL, "pw"));
+ check(kadm5_get_principal(handle, princ, &gent,
+ KADM5_PRINCIPAL_NORMAL_MASK |
+ KADM5_KEY_DATA));
+ assert(gent.max_life == 10);
+ assert(gent.max_renewable_life == 20);
+ assert(gent.princ_expire_time == 30);
+ assert(gent.n_key_data == 0);
+ check(kadm5_delete_principal(handle, princ));
+ check(kadm5_free_principal_ent(handle, &gent));
+ check(kadm5_destroy(handle));
+
+ /* Succeeds with incorrect password using local auth. */
+ check(kadm5_init(context, "admin", "wrong", KADM5_ADMIN_SERVICE, NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_destroy(handle));
+
+ /* Succeeds with null service using local auth. */
+ check(kadm5_init(context, "admin", "admin", NULL, NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_destroy(handle));
+
+ /* Succeeds with nonexistent, non-kadmin service using local auth. */
+ check(kadm5_init(context, "admin", "admin", "foobar", NULL,
+ KADM5_STRUCT_VERSION, api, NULL, &handle));
+ check(kadm5_destroy(handle));
+ }
+
+ krb5_free_principal(context, princ);
+}
+
+static void
+mpol_test_fail(char *user, kadm5_policy_ent_t ent, uint32_t mask,
+ krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_modify_policy(handle, ent, mask), code);
+ free_handle(handle);
+}
+
+static void
+mpol_test_compare(void *handle, kadm5_policy_ent_t ent, uint32_t mask)
+{
+ mpol_test_fail(handle, ent, mask, 0);
+ compare_policy(ent, mask);
+}
+
+static void
+test_modify_policy()
+{
+ kadm5_policy_ent_rec ent;
+
+ memset(&ent, 0, sizeof(ent));
+ ent.policy = "modify-policy-test";
+ create_simple_policy(ent.policy);
+
+ /* pw_min_life = 0 and pw_min_life != 0 */
+ mpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
+ ent.pw_min_life = 32;
+ mpol_test_compare("admin", &ent, KADM5_PW_MIN_LIFE);
+
+ /* pw_max_life = 0 and pw_max_life != 0 */
+ mpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
+ ent.pw_max_life = 32;
+ mpol_test_compare("admin", &ent, KADM5_PW_MAX_LIFE);
+
+ /* pw_min_length = 0 (rejected) and pw_min_length != 0 */
+ mpol_test_fail("admin", &ent, KADM5_PW_MIN_LENGTH, KADM5_BAD_LENGTH);
+ ent.pw_min_length = 8;
+ mpol_test_compare("admin", &ent, KADM5_PW_MIN_LENGTH);
+
+ /* pw_min_classes = 0 (rejected), 1, 5, 6 (rejected) */
+ mpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
+ ent.pw_min_classes = 1;
+ mpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
+ ent.pw_min_classes = 5;
+ mpol_test_compare("admin", &ent, KADM5_PW_MIN_CLASSES);
+ ent.pw_min_classes = 6;
+ mpol_test_fail("admin", &ent, KADM5_PW_MIN_CLASSES, KADM5_BAD_CLASS);
+
+ /* pw_history_num = 0 (rejected), 1, 10 */
+ mpol_test_fail("admin", &ent, KADM5_PW_HISTORY_NUM, KADM5_BAD_HISTORY);
+ ent.pw_history_num = 1;
+ mpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
+ ent.pw_history_num = 10;
+ mpol_test_compare("admin", &ent, KADM5_PW_HISTORY_NUM);
+
+ if (api >= KADM5_API_VERSION_3) {
+ ent.pw_max_fail = 2;
+ mpol_test_compare("admin", &ent, KADM5_PW_MAX_FAILURE);
+ ent.pw_failcnt_interval = 90;
+ mpol_test_compare("admin", &ent, KADM5_PW_FAILURE_COUNT_INTERVAL);
+ ent.pw_lockout_duration = 180;
+ mpol_test_compare("admin", &ent, KADM5_PW_LOCKOUT_DURATION);
+ }
+
+ /* Fails over RPC if "modify" ACL is not granted, or if we authenticated to
+ * kadmin/changepw. */
+ if (rpc) {
+ mpol_test_fail("$admin", &ent, KADM5_PW_MAX_LIFE, KADM5_AUTH_MODIFY);
+ mpol_test_fail("admin/none", &ent, KADM5_PW_MAX_LIFE,
+ KADM5_AUTH_MODIFY);
+ mpol_test_fail("admin/get", &ent, KADM5_PW_MAX_LIFE,
+ KADM5_AUTH_MODIFY);
+ mpol_test_compare("admin/modify", &ent, KADM5_PW_MAX_LIFE);
+ }
+
+ delete_policy(ent.policy);
+
+ /* Fails with empty or null policy name. */
+ ent.policy = NULL;
+ mpol_test_fail("admin", &ent, KADM5_PW_MAX_LIFE, EINVAL);
+ ent.policy = "";
+ mpol_test_fail("admin", &ent, KADM5_PW_MAX_LIFE, KADM5_BAD_POLICY);
+
+ /* Fails with null handle or policy ent. */
+ mpol_test_fail(NULL, &ent, KADM5_PW_MAX_LIFE, KADM5_BAD_SERVER_HANDLE);
+ mpol_test_fail("admin", NULL, KADM5_PW_MAX_LIFE, EINVAL);
+}
+
+static void
+mprinc_test_fail(char *user, kadm5_principal_ent_t ent, uint32_t mask,
+ krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_modify_principal(handle, ent, mask), code);
+ free_handle(handle);
+}
+
+static void
+mprinc_test_compare(char *user, kadm5_principal_ent_t ent, uint32_t mask)
+{
+ mprinc_test_fail(user, ent, mask, 0);
+ compare_princ(ent, mask);
+}
+
+static void
+test_modify_principal()
+{
+ void *handle;
+ krb5_principal princ = parse_princ("modify-principal-test");
+ kadm5_principal_ent_rec ent;
+ krb5_tl_data tl = { NULL, 1, 1, (uint8_t *)"x" };
+ krb5_tl_data tl2 = { NULL, 999, 6, (uint8_t *)"foobar" };
+
+ memset(&ent, 0, sizeof(ent));
+ ent.principal = princ;
+
+ /* Fails with unknown principal. */
+ mprinc_test_fail("admin", &ent, KADM5_KVNO, KADM5_UNK_PRINC);
+
+ create_simple_princ(princ, NULL);
+
+ /* Fails with prohibited mask bit or tl-data type. */
+ mprinc_test_fail("admin", &ent, KADM5_AUX_ATTRIBUTES, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_KEY_DATA, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_LAST_FAILED, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_LAST_SUCCESS, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_LAST_PWD_CHANGE, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_MKVNO, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_MOD_NAME, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_MOD_TIME, KADM5_BAD_MASK);
+ mprinc_test_fail("admin", &ent, KADM5_PRINCIPAL, KADM5_BAD_MASK);
+
+ /* Fails with tl-data type below 256. */
+ ent.n_tl_data = 1;
+ ent.tl_data = &tl;
+ mprinc_test_fail("admin", &ent, KADM5_TL_DATA, KADM5_BAD_TL_TYPE);
+
+ /* Fails with fail_auth_count other than zero. */
+ ent.fail_auth_count = 1234;
+ mprinc_test_fail("admin", &ent, KADM5_FAIL_AUTH_COUNT,
+ KADM5_BAD_SERVER_PARAMS);
+ ent.fail_auth_count = 0;
+
+ /* Succeeds with zero values of various fields. */
+ mprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION);
+ mprinc_test_compare("admin", &ent, KADM5_MAX_LIFE);
+ mprinc_test_compare("admin", &ent, KADM5_MAX_RLIFE);
+ mprinc_test_compare("admin", &ent, KADM5_FAIL_AUTH_COUNT);
+ mprinc_test_compare("admin/modify", &ent, KADM5_PRINC_EXPIRE_TIME);
+ mprinc_test_compare("admin", &ent, KADM5_POLICY_CLR);
+
+ /* Setting a policy causes a pw_expiration computation. Explicit
+ * PW_EXPIRATION overrides the policy. */
+ ent.pw_expiration = 1234;
+ mprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION);
+ ent.policy = "dict-only-pol";
+ mprinc_test_compare("admin", &ent, KADM5_POLICY);
+ ent.policy = "test-pol";
+ mprinc_test_compare("admin", &ent, KADM5_POLICY);
+ ent.pw_expiration = 999999999;
+ mprinc_test_compare("admin", &ent, KADM5_PW_EXPIRATION);
+ mprinc_test_compare("admin", &ent, KADM5_POLICY_CLR);
+
+ /* Succeeds with non-zero values of various fields. */
+ ent.princ_expire_time = 1234;
+ mprinc_test_compare("admin", &ent, KADM5_PRINC_EXPIRE_TIME);
+ ent.attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+ mprinc_test_compare("admin", &ent, KADM5_ATTRIBUTES);
+ ent.attributes = KRB5_KDB_REQUIRES_PWCHANGE;
+ mprinc_test_compare("admin", &ent, KADM5_ATTRIBUTES);
+ ent.attributes = KRB5_KDB_DISALLOW_TGT_BASED;
+ mprinc_test_compare("admin", &ent, KADM5_ATTRIBUTES);
+ ent.max_life = 3456;
+ mprinc_test_compare("admin", &ent, KADM5_MAX_LIFE);
+ ent.kvno = 7;
+ mprinc_test_compare("admin", &ent, KADM5_KVNO);
+
+ /* Fails over RPC if "modify" ACL is not granted, or if we authenticated to
+ * kadmin/changepw. */
+ if (rpc) {
+ mprinc_test_fail("$admin", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
+ mprinc_test_fail("admin/none", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
+ mprinc_test_fail("admin/get", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
+ mprinc_test_fail("admin/add", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
+ mprinc_test_fail("admin/delete", &ent, KADM5_KVNO, KADM5_AUTH_MODIFY);
+ }
+
+ /* tl-data of type > 255 is accepted. */
+ handle = get_handle("admin");
+ ent.max_renewable_life = 88;
+ ent.tl_data = &tl2;
+ check(kadm5_modify_principal(handle, &ent,
+ KADM5_MAX_RLIFE | KADM5_TL_DATA));
+ memset(&ent, 0, sizeof(ent));
+ check(kadm5_get_principal(handle, princ, &ent,
+ KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA));
+ assert(ent.max_renewable_life == 88);
+ assert(ent.n_tl_data == 1);
+ assert(ent.tl_data->tl_data_type == tl2.tl_data_type);
+ assert(ent.tl_data->tl_data_length == tl2.tl_data_length);
+ assert(memcmp(ent.tl_data->tl_data_contents, tl2.tl_data_contents,
+ tl2.tl_data_length) == 0);
+ check(kadm5_free_principal_ent(handle, &ent));
+ free_handle(handle);
+
+ /* Fails with null handle or principal ent. */
+ mprinc_test_fail(NULL, &ent, KADM5_KVNO, KADM5_BAD_SERVER_HANDLE);
+ mprinc_test_fail("admin", NULL, KADM5_KVNO, EINVAL);
+
+ delete_princ(princ);
+ krb5_free_principal(context, princ);
+}
+
+static void
+rnd_test_fail(char *user, krb5_principal princ, krb5_error_code code)
+{
+ void *handle = get_handle(user);
+
+ check_fail(kadm5_randkey_principal(handle, princ, NULL, NULL), code);
+ free_handle(handle);
+}
+
+static void
+rnd_test_succeed(char *user, krb5_principal princ)
+{
+ rnd_test_fail(user, princ, 0);
+}
+
+static void
+test_randkey()
+{
+ void *handle;
+ krb5_principal princ = parse_princ("randkey-principal-test");
+ krb5_principal user_princ = parse_princ("user");
+ krb5_principal admin_princ = parse_princ("admin");
+ kadm5_principal_ent_rec ent;
+ krb5_keyblock *keys;
+ int n_keys, i;
+
+ create_simple_princ(princ, NULL);
+
+ /* Check kvno and enctypes after randkey. */
+ handle = get_handle("admin");
+ check(kadm5_randkey_principal(handle, princ, &keys, &n_keys));
+ check(kadm5_get_principal(handle, princ, &ent, KADM5_KEY_DATA));
+ compare_key_data(&ent, default_supported_enctypes);
+ assert(ent.key_data[0].key_data_kvno == 2);
+ assert(n_keys == ent.n_key_data);
+ for (i = 0; i < n_keys; i++)
+ krb5_free_keyblock_contents(context, &keys[i]);
+ free(keys);
+ check(kadm5_free_principal_ent(handle, &ent));
+ free_handle(handle);
+
+ /*
+ * Fails over RPC if "change" ACL is not granted, or if we authenticated to
+ * kadmin/changepw and are changing another principal's password, or for
+ * self-service if the policy minimum life has not elapsed since the last
+ * key change.
+ */
+ if (rpc) {
+ rnd_test_fail("$admin", user_princ, KADM5_AUTH_CHANGEPW);
+ rnd_test_fail("admin/none", user_princ, KADM5_AUTH_CHANGEPW);
+ rnd_test_fail("admin/delete", user_princ, KADM5_AUTH_CHANGEPW);
+ rnd_test_succeed("admin/modify", user_princ);
+ cpw_test_succeed("admin", user_princ, USER_PASSWORD);
+ rnd_test_fail("user", user_princ, KADM5_PASS_TOOSOON);
+ rnd_test_fail("$user", user_princ, KADM5_PASS_TOOSOON);
+ }
+
+ /* Succeeds with change privilege in spite of policy minimum life. */
+ rnd_test_succeed("admin/modify", user_princ);
+ cpw_test_succeed("admin", user_princ, USER_PASSWORD);
+
+ /* Succeeds for self-service when authenticating to kadmin/changepw. */
+ handle = get_handle("$admin");
+ check(kadm5_randkey_principal(handle, admin_princ, NULL, NULL));
+ check(kadm5_chpass_principal(handle, admin_princ, ADMIN_PASSWORD));
+ free_handle(handle);
+
+ /* Fails with null handle or principal name. */
+ rnd_test_fail(NULL, princ, KADM5_BAD_SERVER_HANDLE);
+ rnd_test_fail("admin", NULL, EINVAL);
+
+ delete_princ(princ);
+ krb5_free_principal(context, princ);
+ krb5_free_principal(context, user_princ);
+ krb5_free_principal(context, admin_princ);
+}
+
+int
+main(int argc, char **argv)
+{
+ assert(argc == 2);
+ rpc = (strcmp(argv[1], "clnt") == 0);
+
+ check(kadm5_init_krb5_context(&context));
+
+ api = KADM5_API_VERSION_2;
+ test_create_policy();
+ test_get_policy();
+ test_modify_policy();
+
+ api = KADM5_API_VERSION_4;
+ test_chpass();
+ test_create_policy();
+ test_create_principal();
+ test_delete_policy();
+ test_delete_principal();
+ test_get_policy();
+ test_get_principal();
+ test_init_destroy();
+ test_modify_policy();
+ test_modify_principal();
+ test_randkey();
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/src/lib/kadm5/t_kadm5.py b/src/lib/kadm5/t_kadm5.py
new file mode 100644
index 0000000..c218b67
--- /dev/null
+++ b/src/lib/kadm5/t_kadm5.py
@@ -0,0 +1,45 @@
+from k5test import *
+
+# Specify a supported_enctypes so the chpass tests know what to expect.
+supported_enctypes = 'aes256-cts:normal aes128-cts:normal'
+conf = {'realms': {'$realm': {'supported_enctypes': supported_enctypes}}}
+realm = K5Realm(create_user=False, create_host=False, kdc_conf=conf)
+
+with open(os.path.join(realm.testdir, 'acl'), 'w') as f:
+ f.write('''
+admin admcilse
+admin/get il
+admin/modify mc
+admin/delete d
+admin/add a
+admin/rename adil
+''')
+
+with open(os.path.join(realm.testdir, 'dictfile'), 'w') as f:
+ f.write('''
+Abyssinia
+Discordianism
+foo
+''')
+
+realm.start_kadmind()
+
+realm.run([kadminl, 'addpol', '-maxlife', '10000s', '-minlength', '8',
+ '-minclasses', '2', '-maxfailure', '2',
+ '-failurecountinterval', '90s', '-lockoutduration', '180s',
+ 'test-pol'])
+realm.run([kadminl, 'addpol', '-minlife', '10s', 'minlife-pol'])
+realm.run([kadminl, 'addpol', 'dict-only-pol'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin/get'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin/modify'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin/delete'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin/add'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin/rename'])
+realm.run([kadminl, 'addprinc', '-pw', 'admin', 'admin/none'])
+realm.run([kadminl, 'addprinc', '-pw', 'us3r', '-policy', 'minlife-pol',
+ 'user'])
+
+realm.run(['./t_kadm5srv', 'srv'])
+realm.run(['./t_kadm5clnt', 'clnt'])
+success('kadm5 API tests')
diff --git a/src/util/k5test.py b/src/util/k5test.py
index 54889ed..c26bc69 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -1277,7 +1277,7 @@ _default_kdc_conf = {
'iprop_port': '$port4',
'key_stash_file': '$testdir/stash',
'acl_file': '$testdir/acl',
- 'dictfile': '$testdir/dictfile',
+ 'dict_file': '$testdir/dictfile',
'kadmind_port': '$port1',
'kpasswd_port': '$port2',
'kdc_listen': '$port0',
More information about the cvs-krb5
mailing list