diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/kadm5/admin.h | 32 | ||||
-rw-r--r-- | src/lib/kadm5/admin_internal.h | 2 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/client_init.c | 12 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/clnt_policy.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/kadm_err.et | 1 | ||||
-rw-r--r-- | src/lib/kadm5/kadm_rpc_xdr.c | 40 | ||||
-rw-r--r-- | src/lib/kadm5/misc_free.c | 15 | ||||
-rw-r--r-- | src/lib/kadm5/srv/server_init.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/srv/svr_policy.c | 190 | ||||
-rw-r--r-- | src/lib/kadm5/srv/svr_principal.c | 258 | ||||
-rw-r--r-- | src/lib/kadm5/unit-test/destroy-test.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/unit-test/handle-test.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/unit-test/init-test.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/unit-test/iter-test.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/unit-test/randkey-test.c | 2 | ||||
-rw-r--r-- | src/lib/kadm5/unit-test/setkey-test.c | 2 | ||||
-rw-r--r-- | src/lib/kdb/kdb5.c | 20 | ||||
-rw-r--r-- | src/lib/kdb/libkdb5.exports | 1 |
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, ¶ms, - 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 |