summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/kadm5/admin.h32
-rw-r--r--src/lib/kadm5/admin_internal.h2
-rw-r--r--src/lib/kadm5/clnt/client_init.c12
-rw-r--r--src/lib/kadm5/clnt/clnt_policy.c2
-rw-r--r--src/lib/kadm5/kadm_err.et1
-rw-r--r--src/lib/kadm5/kadm_rpc_xdr.c40
-rw-r--r--src/lib/kadm5/misc_free.c15
-rw-r--r--src/lib/kadm5/srv/server_init.c2
-rw-r--r--src/lib/kadm5/srv/svr_policy.c190
-rw-r--r--src/lib/kadm5/srv/svr_principal.c258
-rw-r--r--src/lib/kadm5/unit-test/destroy-test.c2
-rw-r--r--src/lib/kadm5/unit-test/handle-test.c2
-rw-r--r--src/lib/kadm5/unit-test/init-test.c2
-rw-r--r--src/lib/kadm5/unit-test/iter-test.c2
-rw-r--r--src/lib/kadm5/unit-test/randkey-test.c2
-rw-r--r--src/lib/kadm5/unit-test/setkey-test.c2
-rw-r--r--src/lib/kdb/kdb5.c20
-rw-r--r--src/lib/kdb/libkdb5.exports1
18 files changed, 504 insertions, 83 deletions
diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h
index 020962b09..037e2f96e 100644
--- a/src/lib/kadm5/admin.h
+++ b/src/lib/kadm5/admin.h
@@ -116,15 +116,20 @@ typedef long kadm5_ret_t;
/* kadm5_policy_ent_t */
-#define KADM5_PW_MAX_LIFE 0x004000
-#define KADM5_PW_MIN_LIFE 0x008000
-#define KADM5_PW_MIN_LENGTH 0x010000
-#define KADM5_PW_MIN_CLASSES 0x020000
-#define KADM5_PW_HISTORY_NUM 0x040000
-#define KADM5_REF_COUNT 0x080000
-#define KADM5_PW_MAX_FAILURE 0x100000
-#define KADM5_PW_FAILURE_COUNT_INTERVAL 0x200000
-#define KADM5_PW_LOCKOUT_DURATION 0x400000
+#define KADM5_PW_MAX_LIFE 0x00004000
+#define KADM5_PW_MIN_LIFE 0x00008000
+#define KADM5_PW_MIN_LENGTH 0x00010000
+#define KADM5_PW_MIN_CLASSES 0x00020000
+#define KADM5_PW_HISTORY_NUM 0x00040000
+#define KADM5_REF_COUNT 0x00080000
+#define KADM5_PW_MAX_FAILURE 0x00100000
+#define KADM5_PW_FAILURE_COUNT_INTERVAL 0x00200000
+#define KADM5_PW_LOCKOUT_DURATION 0x00400000
+#define KADM5_POLICY_ATTRIBUTES 0x00800000
+#define KADM5_POLICY_MAX_LIFE 0x01000000
+#define KADM5_POLICY_MAX_RLIFE 0x02000000
+#define KADM5_POLICY_ALLOWED_KEYSALTS 0x04000000
+#define KADM5_POLICY_TL_DATA 0x08000000
/* kadm5_config_params */
#define KADM5_CONFIG_REALM 0x00000001
@@ -179,6 +184,7 @@ typedef long kadm5_ret_t;
#define KADM5_API_VERSION_MASK 0x12345700
#define KADM5_API_VERSION_2 (KADM5_API_VERSION_MASK|0x02)
#define KADM5_API_VERSION_3 (KADM5_API_VERSION_MASK|0x03)
+#define KADM5_API_VERSION_4 (KADM5_API_VERSION_MASK|0x04)
typedef struct _kadm5_principal_ent_t {
krb5_principal principal;
@@ -218,6 +224,14 @@ typedef struct _kadm5_policy_ent_t {
krb5_kvno pw_max_fail;
krb5_deltat pw_failcnt_interval;
krb5_deltat pw_lockout_duration;
+
+ /* version 4 fields */
+ krb5_flags attributes;
+ krb5_deltat max_life;
+ krb5_deltat max_renewable_life;
+ char *allowed_keysalts;
+ krb5_int16 n_tl_data;
+ krb5_tl_data *tl_data;
} kadm5_policy_ent_rec, *kadm5_policy_ent_t;
/*
diff --git a/src/lib/kadm5/admin_internal.h b/src/lib/kadm5/admin_internal.h
index dc21a65b3..6d79243bd 100644
--- a/src/lib/kadm5/admin_internal.h
+++ b/src/lib/kadm5/admin_internal.h
@@ -32,7 +32,7 @@
return KADM5_BAD_API_VERSION; \
if (srvr->api_version < KADM5_API_VERSION_2) \
return old_api_version; \
- if (srvr->api_version > KADM5_API_VERSION_3) \
+ if (srvr->api_version > KADM5_API_VERSION_4) \
return new_api_version; \
}
diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c
index a8abebfcf..adc050c95 100644
--- a/src/lib/kadm5/clnt/client_init.c
+++ b/src/lib/kadm5/clnt/client_init.c
@@ -197,7 +197,7 @@ init_any(krb5_context context, char *client_name, enum init_type init_type,
handle->destroy_cache = 0;
handle->context = 0;
*handle->lhandle = *handle;
- handle->lhandle->api_version = KADM5_API_VERSION_3;
+ handle->lhandle->api_version = KADM5_API_VERSION_4;
handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
handle->lhandle->lhandle = handle->lhandle;
@@ -337,6 +337,16 @@ init_any(krb5_context context, char *client_name, enum init_type init_type,
#endif
goto error;
}
+ /* Drop down to v3 wire protocol if server does not support v4 */
+ if (r->code == KADM5_NEW_SERVER_API_VERSION &&
+ handle->api_version == KADM5_API_VERSION_4) {
+ handle->api_version = KADM5_API_VERSION_3;
+ r = init_2(&handle->api_version, handle->clnt);
+ if (r == NULL) {
+ code = KADM5_RPC_ERROR;
+ goto error;
+ }
+ }
/* Drop down to v2 wire protocol if server does not support v3 */
if (r->code == KADM5_NEW_SERVER_API_VERSION &&
handle->api_version == KADM5_API_VERSION_3) {
diff --git a/src/lib/kadm5/clnt/clnt_policy.c b/src/lib/kadm5/clnt/clnt_policy.c
index eda354a94..3b3823fde 100644
--- a/src/lib/kadm5/clnt/clnt_policy.c
+++ b/src/lib/kadm5/clnt/clnt_policy.c
@@ -89,6 +89,8 @@ kadm5_get_policy(void *server_handle, char *name, kadm5_policy_ent_t ent)
gpol_ret *r;
kadm5_server_handle_t handle = server_handle;
+ memset(ent, 0, sizeof(*ent));
+
CHECK_HANDLE(server_handle);
arg.name = name;
diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et
index 5530ccafa..c71767013 100644
--- a/src/lib/kadm5/kadm_err.et
+++ b/src/lib/kadm5/kadm_err.et
@@ -62,4 +62,5 @@ error_code KADM5_MISSING_KRB5_CONF_PARAMS, "Missing parameters in krb5.conf requ
error_code KADM5_XDR_FAILURE, "XDR encoding error"
error_code KADM5_CANT_RESOLVE, "Cannot resolve network address for admin server in requested realm"
error_code KADM5_PASS_Q_GENERIC, "Unspecified password quality failure"
+error_code KADM5_BAD_KEYSALTS, "Invalid key/salt tuples"
end
diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c
index 0b0253a5c..153b96297 100644
--- a/src/lib/kadm5/kadm_rpc_xdr.c
+++ b/src/lib/kadm5/kadm_rpc_xdr.c
@@ -492,17 +492,45 @@ _xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp, int vers)
if (!xdr_long(xdrs, &objp->policy_refcnt)) {
return (FALSE);
}
- if (vers == KADM5_API_VERSION_3) {
+ if (xdrs->x_op == XDR_DECODE) {
+ objp->pw_max_fail = 0;
+ objp->pw_failcnt_interval = 0;
+ objp->pw_lockout_duration = 0;
+ objp->attributes = 0;
+ objp->max_life = 0;
+ objp->max_renewable_life = 0;
+ objp->allowed_keysalts = NULL;
+ objp->n_tl_data = 0;
+ objp->tl_data = NULL;
+ }
+ if (vers >= KADM5_API_VERSION_3) {
if (!xdr_krb5_kvno(xdrs, &objp->pw_max_fail))
return (FALSE);
if (!xdr_krb5_deltat(xdrs, &objp->pw_failcnt_interval))
return (FALSE);
if (!xdr_krb5_deltat(xdrs, &objp->pw_lockout_duration))
return (FALSE);
- } else if (xdrs->x_op == XDR_DECODE) {
- objp->pw_max_fail = 0;
- objp->pw_failcnt_interval = 0;
- objp->pw_lockout_duration = 0;
+ }
+ if (vers >= KADM5_API_VERSION_4) {
+ if (!xdr_krb5_flags(xdrs, &objp->attributes)) {
+ return (FALSE);
+ }
+ if (!xdr_krb5_deltat(xdrs, &objp->max_life)) {
+ return (FALSE);
+ }
+ if (!xdr_krb5_deltat(xdrs, &objp->max_renewable_life)) {
+ return (FALSE);
+ }
+ if (!xdr_nullstring(xdrs, &objp->allowed_keysalts)) {
+ return (FALSE);
+ }
+ if (!xdr_krb5_int16(xdrs, &objp->n_tl_data)) {
+ return (FALSE);
+ }
+ if (!xdr_nulltype(xdrs, (void **) &objp->tl_data,
+ xdr_krb5_tl_data)) {
+ return FALSE;
+ }
}
return (TRUE);
}
@@ -510,7 +538,7 @@ _xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp, int vers)
bool_t
xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp)
{
- return _xdr_kadm5_policy_ent_rec(xdrs, objp, KADM5_API_VERSION_3);
+ return _xdr_kadm5_policy_ent_rec(xdrs, objp, KADM5_API_VERSION_4);
}
bool_t
diff --git a/src/lib/kadm5/misc_free.c b/src/lib/kadm5/misc_free.c
index 1d7079199..497b8c2b6 100644
--- a/src/lib/kadm5/misc_free.c
+++ b/src/lib/kadm5/misc_free.c
@@ -11,10 +11,21 @@
kadm5_ret_t
kadm5_free_policy_ent(void *server_handle, kadm5_policy_ent_t val)
{
+ krb5_tl_data *tl_next;
+
_KADM5_CHECK_HANDLE(server_handle);
- if (val)
- free(val->policy);
+ if (val == NULL)
+ return KADM5_OK;
+
+ free(val->policy);
+ free(val->allowed_keysalts);
+ for (; val->tl_data; val->tl_data = tl_next) {
+ tl_next = val->tl_data->tl_data_next;
+ free(val->tl_data->tl_data_contents);
+ free(val->tl_data);
+ }
+ memset(val, 0, sizeof(*val));
return KADM5_OK;
}
diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c
index b170e9e7c..3c3a8795f 100644
--- a/src/lib/kadm5/srv/server_init.c
+++ b/src/lib/kadm5/srv/server_init.c
@@ -283,7 +283,7 @@ kadm5_ret_t kadm5_init(krb5_context context, char *client_name, char *pass,
return ENOMEM;
}
*handle->lhandle = *handle;
- handle->lhandle->api_version = KADM5_API_VERSION_3;
+ handle->lhandle->api_version = KADM5_API_VERSION_4;
handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
handle->lhandle->lhandle = handle->lhandle;
diff --git a/src/lib/kadm5/srv/svr_policy.c b/src/lib/kadm5/srv/svr_policy.c
index 4e0065c3d..3a8f82ecf 100644
--- a/src/lib/kadm5/srv/svr_policy.c
+++ b/src/lib/kadm5/srv/svr_policy.c
@@ -52,6 +52,24 @@ kadm5_create_policy(void *server_handle,
return kadm5_create_policy_internal(server_handle, entry, mask);
}
+/* Validate allowed_keysalts. */
+static kadm5_ret_t
+validate_allowed_keysalts(char *allowed_keysalts)
+{
+ kadm5_ret_t ret;
+ krb5_key_salt_tuple *ks_tuple = NULL;
+ krb5_int32 n_ks_tuple = 0;
+
+ if (strchr(allowed_keysalts, '\t') != NULL)
+ return KADM5_BAD_KEYSALTS;
+ ret = krb5_string_to_keysalts(allowed_keysalts, ",", ":.-", 0,
+ &ks_tuple, &n_ks_tuple);
+ free(ks_tuple);
+ if (ret == EINVAL)
+ return KADM5_BAD_KEYSALTS;
+ return ret;
+}
+
/*
* Function: kadm5_create_policy_internal
*
@@ -89,7 +107,14 @@ kadm5_create_policy_internal(void *server_handle,
return KADM5_BAD_POLICY;
if (!(mask & KADM5_POLICY))
return KADM5_BAD_MASK;
+ if ((mask & KADM5_POLICY_ALLOWED_KEYSALTS) &&
+ entry->allowed_keysalts != NULL) {
+ ret = validate_allowed_keysalts(entry->allowed_keysalts);
+ if (ret)
+ return ret;
+ }
+ memset(&pent, 0, sizeof(pent));
pent.name = entry->policy;
p = entry->policy;
while(*p != '\0') {
@@ -138,7 +163,32 @@ kadm5_create_policy_internal(void *server_handle,
else
pent.policy_refcnt = entry->policy_refcnt;
- if (handle->api_version == KADM5_API_VERSION_3) {
+ if (handle->api_version >= KADM5_API_VERSION_4) {
+ if (!(mask & KADM5_POLICY_ATTRIBUTES))
+ pent.attributes = 0;
+ else
+ pent.attributes = entry->attributes;
+ if (!(mask & KADM5_POLICY_MAX_LIFE))
+ pent.max_life = 0;
+ else
+ pent.max_life = entry->max_life;
+ if (!(mask & KADM5_POLICY_MAX_RLIFE))
+ pent.max_renewable_life = 0;
+ else
+ pent.max_renewable_life = entry->max_renewable_life;
+ if (!(mask & KADM5_POLICY_ALLOWED_KEYSALTS))
+ pent.allowed_keysalts = 0;
+ else
+ pent.allowed_keysalts = entry->allowed_keysalts;
+ if (!(mask & KADM5_POLICY_TL_DATA)) {
+ pent.n_tl_data = 0;
+ pent.tl_data = NULL;
+ } else {
+ pent.n_tl_data = entry->n_tl_data;
+ pent.tl_data = entry->tl_data;
+ }
+ }
+ if (handle->api_version >= KADM5_API_VERSION_3) {
if (!(mask & KADM5_PW_MAX_FAILURE))
pent.pw_max_fail = 0;
else
@@ -151,10 +201,6 @@ kadm5_create_policy_internal(void *server_handle,
pent.pw_lockout_duration = 0;
else
pent.pw_lockout_duration = entry->pw_lockout_duration;
- } else {
- pent.pw_max_fail = 0;
- pent.pw_failcnt_interval = 0;
- pent.pw_lockout_duration = 0;
}
if ((ret = krb5_db_create_policy(handle->context, &pent)))
@@ -209,13 +255,58 @@ kadm5_modify_policy(void *server_handle,
return kadm5_modify_policy_internal(server_handle, entry, mask);
}
+/* Allocate and form a TL data list of a desired size. */
+static int
+alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp)
+{
+ krb5_tl_data **tlp = tldp;
+ int i;
+
+ for (i = 0; i < n_tl_data; i++) {
+ *tlp = calloc(1, sizeof(krb5_tl_data));
+ if (*tlp == NULL)
+ return ENOMEM; /* caller cleans up */
+ memset(*tlp, 0, sizeof(krb5_tl_data));
+ tlp = &((*tlp)->tl_data_next);
+ }
+
+ return 0;
+}
+
+static kadm5_ret_t
+copy_tl_data(krb5_int16 n_tl_data, krb5_tl_data *tl_data,
+ krb5_tl_data **out)
+{
+ kadm5_ret_t ret;
+ krb5_tl_data *tl, *tl_new;
+
+ if ((ret = alloc_tl_data(n_tl_data, out)))
+ return ret; /* caller cleans up */
+
+ tl = tl_data;
+ tl_new = *out;
+ for (; tl; tl = tl->tl_data_next, tl_new = tl_new->tl_data_next) {
+ tl_new->tl_data_contents = malloc(tl->tl_data_length);
+ if (tl_new->tl_data_contents == NULL)
+ return ENOMEM;
+ memcpy(tl_new->tl_data_contents, tl->tl_data_contents,
+ tl->tl_data_length);
+ tl_new->tl_data_type = tl->tl_data_type;
+ tl_new->tl_data_length = tl->tl_data_length;
+ }
+
+ return 0;
+}
+
kadm5_ret_t
kadm5_modify_policy_internal(void *server_handle,
kadm5_policy_ent_t entry, long mask)
{
- kadm5_server_handle_t handle = server_handle;
- osa_policy_ent_t p;
- int ret;
+ kadm5_server_handle_t handle = server_handle;
+ krb5_tl_data *tl;
+ osa_policy_ent_t p;
+ int ret;
+ size_t len;
CHECK_HANDLE(server_handle);
@@ -225,6 +316,20 @@ kadm5_modify_policy_internal(void *server_handle,
return KADM5_BAD_POLICY;
if((mask & KADM5_POLICY))
return KADM5_BAD_MASK;
+ if ((mask & KADM5_POLICY_ALLOWED_KEYSALTS) &&
+ entry->allowed_keysalts != NULL) {
+ ret = validate_allowed_keysalts(entry->allowed_keysalts);
+ if (ret)
+ return ret;
+ }
+ if ((mask & KADM5_POLICY_TL_DATA)) {
+ tl = entry->tl_data;
+ while (tl != NULL) {
+ if (tl->tl_data_type < 256)
+ return KADM5_BAD_TL_TYPE;
+ tl = tl->tl_data_next;
+ }
+ }
ret = krb5_db_get_policy(handle->context, entry->policy, &p);
if (ret == KRB5_KDB_NOENTRY)
@@ -265,7 +370,7 @@ kadm5_modify_policy_internal(void *server_handle,
}
if ((mask & KADM5_REF_COUNT))
p->policy_refcnt = entry->policy_refcnt;
- if (handle->api_version == KADM5_API_VERSION_3) {
+ if (handle->api_version >= KADM5_API_VERSION_3) {
if ((mask & KADM5_PW_MAX_FAILURE))
p->pw_max_fail = entry->pw_max_fail;
if ((mask & KADM5_PW_FAILURE_COUNT_INTERVAL))
@@ -273,7 +378,39 @@ kadm5_modify_policy_internal(void *server_handle,
if ((mask & KADM5_PW_LOCKOUT_DURATION))
p->pw_lockout_duration = entry->pw_lockout_duration;
}
+ if (handle->api_version >= KADM5_API_VERSION_4) {
+ if ((mask & KADM5_POLICY_ATTRIBUTES))
+ p->attributes = entry->attributes;
+ if ((mask & KADM5_POLICY_MAX_LIFE))
+ p->max_life = entry->max_life;
+ if ((mask & KADM5_POLICY_MAX_RLIFE))
+ p->max_renewable_life = entry->max_renewable_life;
+ if ((mask & KADM5_POLICY_ALLOWED_KEYSALTS)) {
+ krb5_db_free(handle->context, p->allowed_keysalts);
+ p->allowed_keysalts = NULL;
+ if (entry->allowed_keysalts != NULL) {
+ len = strlen(entry->allowed_keysalts) + 1;
+ p->allowed_keysalts = krb5_db_alloc(handle->context, NULL,
+ len);
+ if (p->allowed_keysalts == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(p->allowed_keysalts, entry->allowed_keysalts, len);
+ }
+ }
+ if ((mask & KADM5_POLICY_TL_DATA)) {
+ for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
+ ret = krb5_db_update_tl_data(handle->context, &p->n_tl_data,
+ &p->tl_data, tl);
+ if (ret)
+ goto cleanup;
+ }
+ }
+ }
ret = krb5_db_put_policy(handle->context, p);
+
+cleanup:
krb5_db_free_policy(handle->context, p);
return ret;
}
@@ -283,9 +420,11 @@ kadm5_get_policy(void *server_handle, kadm5_policy_t name,
kadm5_policy_ent_t entry)
{
osa_policy_ent_t t;
- int ret;
+ kadm5_ret_t ret;
kadm5_server_handle_t handle = server_handle;
+ memset(entry, 0, sizeof(*entry));
+
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
@@ -301,8 +440,8 @@ kadm5_get_policy(void *server_handle, kadm5_policy_t name,
return ret;
if ((entry->policy = strdup(t->name)) == NULL) {
- krb5_db_free_policy(handle->context, t);
- return ENOMEM;
+ ret = ENOMEM;
+ goto cleanup;
}
entry->pw_min_life = t->pw_min_life;
entry->pw_max_life = t->pw_max_life;
@@ -310,12 +449,33 @@ kadm5_get_policy(void *server_handle, kadm5_policy_t name,
entry->pw_min_classes = t->pw_min_classes;
entry->pw_history_num = t->pw_history_num;
entry->policy_refcnt = t->policy_refcnt;
- if (handle->api_version == KADM5_API_VERSION_3) {
+ if (handle->api_version >= KADM5_API_VERSION_3) {
entry->pw_max_fail = t->pw_max_fail;
entry->pw_failcnt_interval = t->pw_failcnt_interval;
entry->pw_lockout_duration = t->pw_lockout_duration;
}
- krb5_db_free_policy(handle->context, t);
+ if (handle->api_version >= KADM5_API_VERSION_4) {
+ entry->attributes = t->attributes;
+ entry->max_life = t->max_life;
+ entry->max_renewable_life = t->max_renewable_life;
+ if (t->allowed_keysalts) {
+ entry->allowed_keysalts = strdup(t->allowed_keysalts);
+ if (!entry->allowed_keysalts) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ }
+ ret = copy_tl_data(t->n_tl_data, t->tl_data, &entry->tl_data);
+ if (ret)
+ goto cleanup;
+ entry->n_tl_data = t->n_tl_data;
+ }
+
+ ret = 0;
- return KADM5_OK;
+cleanup:
+ if (ret)
+ kadm5_free_policy_ent(handle, entry);
+ krb5_db_free_policy(handle->context, t);
+ return ret;
}
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index f5ea005b2..f405f55ca 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -173,6 +173,138 @@ static void cleanup_key_data(context, count, data)
krb5_db_free(context, data);
}
+/* Check whether a ks_tuple is present in an array of ks_tuples. */
+static krb5_boolean
+ks_tuple_present(int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ krb5_key_salt_tuple *looking_for)
+{
+ int i;
+
+ for (i = 0; i < n_ks_tuple; i++) {
+ if (ks_tuple[i].ks_enctype == looking_for->ks_enctype &&
+ ks_tuple[i].ks_salttype == looking_for->ks_salttype)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Apply the -allowedkeysalts policy (see kadmin(1)'s addpol/modpol
+ * commands). We use the allowed key/salt tuple list as a default if
+ * no ks tuples as provided by the caller. We reject lists that include
+ * key/salts outside the policy. We re-order the requested ks tuples
+ * (which may be a subset of the policy) to reflect the policy order.
+ */
+static kadm5_ret_t
+apply_keysalt_policy(kadm5_server_handle_t handle, const char *policy,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ int *new_n_kstp, krb5_key_salt_tuple **new_kstp)
+{
+ kadm5_ret_t ret;
+ kadm5_policy_ent_rec polent;
+ int ak_n_ks_tuple = 0;
+ int new_n_ks_tuple = 0;
+ krb5_key_salt_tuple *ak_ks_tuple = NULL;
+ krb5_key_salt_tuple *new_ks_tuple = NULL;
+ krb5_key_salt_tuple *subset;
+ int i, m;
+
+ if (new_n_kstp != NULL) {
+ *new_n_kstp = 0;
+ *new_kstp = NULL;
+ }
+
+ memset(&polent, 0, sizeof(polent));
+ if (policy != NULL &&
+ (ret = kadm5_get_policy(handle->lhandle, (char *)policy,
+ &polent)) != KADM5_OK) {
+ if (ret == EINVAL)
+ ret = KADM5_BAD_POLICY;
+ if (ret)
+ goto cleanup;
+ }
+
+ if (polent.allowed_keysalts == NULL && new_n_kstp != NULL) {
+ /* Requested keysalts allowed or default to supported_enctypes. */
+ if (n_ks_tuple == 0) {
+ /* Default to supported_enctypes. */
+ n_ks_tuple = handle->params.num_keysalts;
+ ks_tuple = handle->params.keysalts;
+ }
+ /* Dup the requested or defaulted keysalt tuples. */
+ new_ks_tuple = malloc(n_ks_tuple * sizeof(*new_ks_tuple));
+ if (new_ks_tuple == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ memcpy(new_ks_tuple, ks_tuple, n_ks_tuple * sizeof(*new_ks_tuple));
+ new_n_ks_tuple = n_ks_tuple;
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = krb5_string_to_keysalts(polent.allowed_keysalts,
+ ", ", /* Tuple separators */
+ ":.-", /* Key/salt separators */
+ 0, /* No duplicates */
+ &ak_ks_tuple,
+ &ak_n_ks_tuple);
+ /*
+ * Malformed policy? Shouldn't happen, but it's remotely possible
+ * someday, so we don't assert, just bail.
+ */
+ if (ret)
+ goto cleanup;
+
+ /* Check that the requested ks_tuples are within policy, if we have one. */
+ for (i = 0; i < n_ks_tuple; i++) {
+ if (!ks_tuple_present(ak_n_ks_tuple, ak_ks_tuple, &ks_tuple[i])) {
+ ret = KADM5_BAD_KEYSALTS;
+ goto cleanup;
+ }
+ }
+
+ /* Have policy but no ks_tuple input? Output the policy. */
+ if (n_ks_tuple == 0) {
+ new_n_ks_tuple = ak_n_ks_tuple;
+ new_ks_tuple = ak_ks_tuple;
+ ak_ks_tuple = NULL;
+ goto cleanup;
+ }
+
+ /*
+ * Now filter the policy ks tuples by the requested ones so as to
+ * preserve in the requested sub-set the relative ordering from the
+ * policy. We could optimize this (if (n_ks_tuple == ak_n_ks_tuple)
+ * then skip this), but we don't bother.
+ */
+ subset = calloc(n_ks_tuple, sizeof(*subset));
+ if (subset == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ for (m = 0, i = 0; i < ak_n_ks_tuple && m < n_ks_tuple; i++) {
+ if (ks_tuple_present(n_ks_tuple, ks_tuple, &ak_ks_tuple[i]))
+ subset[m++] = ak_ks_tuple[i];
+ }
+ new_ks_tuple = subset;
+ new_n_ks_tuple = m;
+ ret = 0;
+
+cleanup:
+ kadm5_free_policy_ent(handle->lhandle, &polent);
+ free(ak_ks_tuple);
+
+ if (new_n_kstp != NULL) {
+ *new_n_kstp = new_n_ks_tuple;
+ *new_kstp = new_ks_tuple;
+ } else {
+ free(new_ks_tuple);
+ }
+ return ret;
+}
+
+
/*
* Set *passptr to NULL if the request looks like the first part of a krb5 1.6
* addprinc -randkey operation. The krb5 1.6 dummy password for these requests
@@ -224,6 +356,8 @@ kadm5_create_principal_3(void *server_handle,
kadm5_server_handle_t handle = server_handle;
krb5_keyblock *act_mkey;
krb5_kvno act_kvno;
+ int new_n_ks_tuple = 0;
+ krb5_key_salt_tuple *new_ks_tuple = NULL;
CHECK_HANDLE(server_handle);
@@ -247,12 +381,6 @@ kadm5_create_principal_3(void *server_handle,
if (entry == NULL)
return EINVAL;
- /* Use default keysalts if caller did not provide any. */
- if (n_ks_tuple == 0) {
- ks_tuple = handle->params.keysalts;
- n_ks_tuple = handle->params.num_keysalts;
- }
-
/*
* Check to see if the principal exists
*/
@@ -362,6 +490,16 @@ kadm5_create_principal_3(void *server_handle,
}
}
+ /*
+ * We need to have setup the TL data, so we have strings, so we can
+ * check enctype policy, which is why we check/initialize ks_tuple
+ * this late.
+ */
+ ret = apply_keysalt_policy(handle, entry->policy, n_ks_tuple, ks_tuple,
+ &new_n_ks_tuple, &new_ks_tuple);
+ if (ret)
+ goto cleanup;
+
/* initialize the keys */
ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, &act_kvno,
@@ -370,13 +508,14 @@ kadm5_create_principal_3(void *server_handle,
goto cleanup;
if (password) {
- ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
- password, (mask & KADM5_KVNO)?entry->kvno:1,
+ ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple,
+ new_n_ks_tuple, password,
+ (mask & KADM5_KVNO)?entry->kvno:1,
FALSE, kdb);
} else {
/* Null password means create with random key (new in 1.8). */
ret = krb5_dbe_crk(handle->context, &master_keyblock,
- ks_tuple, n_ks_tuple, FALSE, kdb);
+ new_ks_tuple, new_n_ks_tuple, FALSE, kdb);
}
if (ret)
goto cleanup;
@@ -388,7 +527,7 @@ kadm5_create_principal_3(void *server_handle,
ret = k5_kadm5_hook_create(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, entry, mask,
- n_ks_tuple, ks_tuple, password);
+ new_n_ks_tuple, new_ks_tuple, password);
if (ret)
goto cleanup;
@@ -441,9 +580,10 @@ kadm5_create_principal_3(void *server_handle,
(void) k5_kadm5_hook_create(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask,
- n_ks_tuple, ks_tuple, password);
+ new_n_ks_tuple, new_ks_tuple, password);
cleanup:
+ free(new_ks_tuple);
krb5_db_free_principal(handle->context, kdb);
if (have_polent)
(void) kadm5_free_policy_ent(handle->lhandle, &polent);
@@ -1345,6 +1485,8 @@ kadm5_chpass_principal_3(void *server_handle,
osa_pw_hist_ent hist;
krb5_keyblock *act_mkey, *hist_keyblocks = NULL;
krb5_kvno act_kvno, hist_kvno;
+ int new_n_ks_tuple = 0;
+ krb5_key_salt_tuple *new_ks_tuple = NULL;
CHECK_HANDLE(server_handle);
@@ -1359,15 +1501,14 @@ kadm5_chpass_principal_3(void *server_handle,
principal, hist_princ)) == TRUE)
return KADM5_PROTECT_PRINCIPAL;
- /* Use default keysalts if caller did not provide any. */
- if (n_ks_tuple == 0) {
- ks_tuple = handle->params.keysalts;
- n_ks_tuple = handle->params.num_keysalts;
- }
-
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
+ ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
+ &new_n_ks_tuple, &new_ks_tuple);
+ if (ret)
+ goto done;
+
if ((adb.aux_attributes & KADM5_POLICY)) {
if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
goto done;
@@ -1392,7 +1533,7 @@ kadm5_chpass_principal_3(void *server_handle,
if (ret)
goto done;
- ret = krb5_dbe_cpw(handle->context, act_mkey, ks_tuple, n_ks_tuple,
+ ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
password, 0 /* increment kvno */,
keepold, kdb);
if (ret)
@@ -1504,7 +1645,7 @@ kadm5_chpass_principal_3(void *server_handle,
ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
- n_ks_tuple, ks_tuple, password);
+ new_n_ks_tuple, new_ks_tuple, password);
if (ret)
goto done;
@@ -1513,9 +1654,10 @@ kadm5_chpass_principal_3(void *server_handle,
(void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, principal,
- keepold, n_ks_tuple, ks_tuple, password);
+ keepold, new_n_ks_tuple, new_ks_tuple, password);
ret = KADM5_OK;
done:
+ free(new_ks_tuple);
if (!hist_added && hist.key_data)
free_history_entry(handle->context, &hist);
kdb_free_entry(handle, kdb, &adb);
@@ -1554,39 +1696,41 @@ kadm5_randkey_principal_3(void *server_handle,
int ret, last_pwd, have_pol = 0;
kadm5_server_handle_t handle = server_handle;
krb5_keyblock *act_mkey;
+ int new_n_ks_tuple = 0;
+ krb5_key_salt_tuple *new_ks_tuple = NULL;
if (keyblocks)
*keyblocks = NULL;
CHECK_HANDLE(server_handle);
- /* Use default keysalts if caller did not provide any. */
- if (n_ks_tuple == 0) {
- ks_tuple = handle->params.keysalts;
- n_ks_tuple = handle->params.num_keysalts;
- }
-
krb5_clear_error_message(handle->context);
if (principal == NULL)
return EINVAL;
+
+ if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+ return(ret);
+
+ ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
+ &new_n_ks_tuple, &new_ks_tuple);
+ if (ret)
+ goto done;
+
if (krb5_principal_compare(handle->context, principal, hist_princ)) {
/* If changing the history entry, the new entry must have exactly one
* key. */
if (keepold)
return KADM5_PROTECT_PRINCIPAL;
- n_ks_tuple = 1;
+ new_n_ks_tuple = 1;
}
- if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
- return(ret);
-
ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list, NULL,
&act_mkey);
if (ret)
goto done;
- ret = krb5_dbe_crk(handle->context, act_mkey, ks_tuple, n_ks_tuple,
+ ret = krb5_dbe_crk(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
keepold, kdb);
if (ret)
goto done;
@@ -1650,7 +1794,7 @@ kadm5_randkey_principal_3(void *server_handle,
ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
- n_ks_tuple, ks_tuple, NULL);
+ new_n_ks_tuple, new_ks_tuple, NULL);
if (ret)
goto done;
if ((ret = kdb_put_entry(handle, kdb, &adb)))
@@ -1658,9 +1802,10 @@ kadm5_randkey_principal_3(void *server_handle,
(void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, principal,
- keepold, n_ks_tuple, ks_tuple, NULL);
+ keepold, new_n_ks_tuple, new_ks_tuple, NULL);
ret = KADM5_OK;
done:
+ free(new_ks_tuple);
kdb_free_entry(handle, kdb, &adb);
if (have_pol)
kadm5_free_policy_ent(handle->lhandle, &pol);
@@ -1838,6 +1983,24 @@ kadm5_setkey_principal(void *server_handle,
keyblocks, n_keys);
}
+/* Make key/salt list from keys for kadm5_setkey_principal_3() */
+static kadm5_ret_t
+make_ks_from_keys(krb5_context context, int n_keys, krb5_keyblock *keyblocks,
+ krb5_key_salt_tuple **ks_tuple)
+{
+ int i;
+
+ *ks_tuple = calloc(n_keys, sizeof(**ks_tuple));
+ if (ks_tuple == NULL)
+ return ENOMEM;
+
+ for (i = 0; i < n_keys; i++) {
+ (*ks_tuple)[i].ks_enctype = keyblocks[i].enctype;
+ (*ks_tuple)[i].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
+ }
+ return 0;
+}
+
kadm5_ret_t
kadm5_setkey_principal_3(void *server_handle,
krb5_principal principal,
@@ -1862,6 +2025,7 @@ kadm5_setkey_principal_3(void *server_handle,
krb5_key_data tmp_key_data;
krb5_key_data *tptr;
krb5_keyblock *act_mkey;
+ krb5_key_salt_tuple *ks_from_keys = NULL;
CHECK_HANDLE(server_handle);
@@ -1874,6 +2038,31 @@ kadm5_setkey_principal_3(void *server_handle,
principal, hist_princ)) == TRUE))
return KADM5_PROTECT_PRINCIPAL;
+ if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+ return(ret);
+
+ if (!n_ks_tuple) {
+ /* Apply policy to the key/salt types implied by the given keys */
+ ret = make_ks_from_keys(handle->context, n_keys, keyblocks,
+ &ks_from_keys);
+ if (ret)
+ goto done;
+ ret = apply_keysalt_policy(handle, adb.policy, n_keys, ks_from_keys,
+ NULL, NULL);
+ free(ks_from_keys);
+ } else {
+ /*
+ * Apply policy to the given ks_tuples. Note that further below
+ * we enforce keyblocks[i].enctype == ks_tuple[i].ks_enctype for
+ * all i from 0 to n_keys, and that n_ks_tuple == n_keys if ks
+ * tuples are given.
+ */
+ ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
+ NULL, NULL);
+ }
+ if (ret)
+ goto done;
+
for (i = 0; i < n_keys; i++) {
for (j = i+1; j < n_keys; j++) {
if ((ret = krb5_c_enctype_compare(handle->context,
@@ -1894,9 +2083,6 @@ kadm5_setkey_principal_3(void *server_handle,
if (n_ks_tuple && n_ks_tuple != n_keys)
return KADM5_SETKEY3_ETYPE_MISMATCH;
- if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
- return(ret);
-
for (kvno = 0, i=0; i<kdb->n_key_data; i++)
if (kdb->key_data[i].key_data_kvno > kvno)
kvno = kdb->key_data[i].key_data_kvno;
diff --git a/src/lib/kadm5/unit-test/destroy-test.c b/src/lib/kadm5/unit-test/destroy-test.c
index ccca24928..738cfeb86 100644
--- a/src/lib/kadm5/unit-test/destroy-test.c
+++ b/src/lib/kadm5/unit-test/destroy-test.c
@@ -28,7 +28,7 @@ int main()
}
for(x = 0; x < TEST_NUM; x++) {
ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, 0,
- KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
&server_handle);
if(ret != KADM5_OK) {
com_err("test", ret, "init");
diff --git a/src/lib/kadm5/unit-test/handle-test.c b/src/lib/kadm5/unit-test/handle-test.c
index a77bc1195..29bd2c9a1 100644
--- a/src/lib/kadm5/unit-test/handle-test.c
+++ b/src/lib/kadm5/unit-test/handle-test.c
@@ -31,7 +31,7 @@ int main(int argc, char *argv[])
kadm5_init_krb5_context(&context);
ret = kadm5_init(context, "admin/none", "admin", KADM5_ADMIN_SERVICE, NULL,
- KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
&server_handle);
if(ret != KADM5_OK) {
com_err("test", ret, "init");
diff --git a/src/lib/kadm5/unit-test/init-test.c b/src/lib/kadm5/unit-test/init-test.c
index 354b812b6..880400c9d 100644
--- a/src/lib/kadm5/unit-test/init-test.c
+++ b/src/lib/kadm5/unit-test/init-test.c
@@ -21,7 +21,7 @@ int main()
exit(1);
}
ret = kadm5_init(context, "admin", "admin", NULL, &params,
- KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
&server_handle);
if (ret == KADM5_RPC_ERROR) {
krb5_free_context(context);
diff --git a/src/lib/kadm5/unit-test/iter-test.c b/src/lib/kadm5/unit-test/iter-test.c
index bc7cfdcfa..cd85ebe4d 100644
--- a/src/lib/kadm5/unit-test/iter-test.c
+++ b/src/lib/kadm5/unit-test/iter-test.c
@@ -23,7 +23,7 @@ int main(int argc, char **argv)
exit(1);
}
ret = kadm5_init("admin", "admin", KADM5_ADMIN_SERVICE, 0,
- KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
&server_handle);
if (ret != KADM5_OK) {
com_err("iter-test", ret, "while initializing");
diff --git a/src/lib/kadm5/unit-test/randkey-test.c b/src/lib/kadm5/unit-test/randkey-test.c
index 7cf4ee8ac..dbef88ac8 100644
--- a/src/lib/kadm5/unit-test/randkey-test.c
+++ b/src/lib/kadm5/unit-test/randkey-test.c
@@ -24,7 +24,7 @@ int main()
krb5_parse_name(context, "testuser", &tprinc);
ret = kadm5_init(context, "admin", "admin", KADM5_ADMIN_SERVICE, NULL,
- KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
&server_handle);
if(ret != KADM5_OK) {
com_err("test", ret, "init");
diff --git a/src/lib/kadm5/unit-test/setkey-test.c b/src/lib/kadm5/unit-test/setkey-test.c
index 53056e434..c1b9c5d1f 100644
--- a/src/lib/kadm5/unit-test/setkey-test.c
+++ b/src/lib/kadm5/unit-test/setkey-test.c
@@ -120,7 +120,7 @@ main(int argc, char **argv)
}
ret = kadm5_init(context, authprinc, NULL, KADM5_ADMIN_SERVICE, NULL,
- KADM5_STRUCT_VERSION, KADM5_API_VERSION_3, NULL,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL,
&handle);
if (ret) {
com_err(whoami, ret, "while initializing connection");
diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c
index a3c2a5f9c..3cf116b76 100644
--- a/src/lib/kdb/kdb5.c
+++ b/src/lib/kdb/kdb5.c
@@ -2185,8 +2185,8 @@ krb5_dbe_delete_tl_data(krb5_context context, krb5_db_entry *entry,
}
krb5_error_code
-krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry,
- krb5_tl_data *new_tl_data)
+krb5_db_update_tl_data(krb5_context context, krb5_int16 *n_tl_datap,
+ krb5_tl_data **tl_datap, krb5_tl_data *new_tl_data)
{
krb5_tl_data *tl_data = NULL;
krb5_octet *tmp;
@@ -2206,7 +2206,7 @@ krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry,
*/
if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) { /* db_args can be multiple */
- for (tl_data = entry->tl_data; tl_data;
+ for (tl_data = *tl_datap; tl_data;
tl_data = tl_data->tl_data_next)
if (tl_data->tl_data_type == new_tl_data->tl_data_type)
break;
@@ -2221,9 +2221,9 @@ krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry,
return (ENOMEM);
}
memset(tl_data, 0, sizeof(krb5_tl_data));
- tl_data->tl_data_next = entry->tl_data;
- entry->tl_data = tl_data;
- entry->n_tl_data++;
+ tl_data->tl_data_next = *tl_datap;
+ *tl_datap = tl_data;
+ (*n_tl_datap)++;
}
/* fill in the record */
@@ -2240,6 +2240,14 @@ krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry,
}
krb5_error_code
+krb5_dbe_update_tl_data(krb5_context context, krb5_db_entry *entry,
+ krb5_tl_data *new_tl_data)
+{
+ return krb5_db_update_tl_data(context, &entry->n_tl_data, &entry->tl_data,
+ new_tl_data);
+}
+
+krb5_error_code
krb5_dbe_compute_salt(krb5_context context, const krb5_key_data *key,
krb5_const_principal princ, krb5_int16 *salttype_out,
krb5_data **salt_out)
diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports
index 0e58262b2..9aa8d1a4a 100644
--- a/src/lib/kdb/libkdb5.exports
+++ b/src/lib/kdb/libkdb5.exports
@@ -65,6 +65,7 @@ krb5_dbe_update_mkey_aux
krb5_dbe_update_mkvno
krb5_dbe_update_mod_princ_data
krb5_dbe_update_tl_data
+krb5_db_update_tl_data
krb5_dbe_def_encrypt_key_data
krb5_dbe_def_decrypt_key_data
krb5_dbe_decrypt_key_data