diff options
author | Simo Sorce <simo@redhat.com> | 2015-12-15 14:49:22 -0500 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2016-02-19 15:39:18 -0500 |
commit | 1d4e83625f1c8cde7638702ab404f4594da3f062 (patch) | |
tree | 6118ec4a219de0714e7bbb26c8488f6bda686412 /src | |
parent | 2e9f19882c1e127fd7d9b09f9d6c3331ee638bfd (diff) | |
download | krb5-1d4e83625f1c8cde7638702ab404f4594da3f062.zip krb5-1d4e83625f1c8cde7638702ab404f4594da3f062.tar.gz krb5-1d4e83625f1c8cde7638702ab404f4594da3f062.tar.bz2 |
Add kadm5_setkey_principal_4 RPC to kadmin
This new version of the RPC allows a user to set not only the
keyblocks but also the kvno and the salts of a key.
ticket: 8355 (new)
Diffstat (limited to 'src')
-rw-r--r-- | src/kadmin/server/kadm_rpc_svc.c | 7 | ||||
-rw-r--r-- | src/kadmin/server/server_stubs.c | 61 | ||||
-rw-r--r-- | src/lib/kadm5/admin.h | 12 | ||||
-rw-r--r-- | src/lib/kadm5/admin_xdr.h | 2 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/client_principal.c | 27 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/client_rpc.c | 15 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/libkadm5clnt_mit.exports | 3 | ||||
-rw-r--r-- | src/lib/kadm5/kadm_err.et | 1 | ||||
-rw-r--r-- | src/lib/kadm5/kadm_rpc.h | 14 | ||||
-rw-r--r-- | src/lib/kadm5/kadm_rpc_xdr.c | 35 | ||||
-rw-r--r-- | src/lib/kadm5/srv/libkadm5srv_mit.exports | 2 | ||||
-rw-r--r-- | src/lib/kadm5/srv/svr_principal.c | 195 |
12 files changed, 374 insertions, 0 deletions
diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c index f4d2a7c..dee3938 100644 --- a/src/kadmin/server/kadm_rpc_svc.c +++ b/src/kadmin/server/kadm_rpc_svc.c @@ -58,6 +58,7 @@ void kadm_1(rqstp, transp) chpass3_arg chpass_principal3_2_arg; chrand3_arg chrand_principal3_2_arg; setkey3_arg setkey_principal3_2_arg; + setkey4_arg setkey_principal4_2_arg; } argument; char *result; bool_t (*xdr_argument)(), (*xdr_result)(); @@ -222,6 +223,12 @@ void kadm_1(rqstp, transp) local = (char *(*)()) set_string_2_svc; break; + case SETKEY_PRINCIPAL4: + xdr_argument = xdr_setkey4_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) setkey_principal4_2_svc; + break; + default: krb5_klog_syslog(LOG_ERR, "Invalid KADM5 procedure number: %s, %d", client_addr(rqstp->rq_xprt), rqstp->rq_proc); diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c index 6ac797e..673cc2e 100644 --- a/src/kadmin/server/server_stubs.c +++ b/src/kadmin/server/server_stubs.c @@ -1100,6 +1100,67 @@ exit_func: return &ret; } +generic_ret * +setkey_principal4_2_svc(setkey4_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + xdr_free(xdr_generic_ret, &ret); + + if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) + goto exit_func; + + if ((ret.code = check_handle((void *)handle))) + goto exit_func; + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + goto exit_func; + } + if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { + ret.code = KADM5_BAD_PRINCIPAL; + goto exit_func; + } + + if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_SETKEY, + arg->princ, NULL)) { + ret.code = kadm5_setkey_principal_4((void *)handle, arg->princ, + arg->keepold, arg->key_data, + arg->n_key_data); + } else { + log_unauth("kadm5_setkey_principal", prime_arg, &client_name, + &service_name, rqstp); + ret.code = KADM5_AUTH_SETKEY; + } + + if (ret.code != KADM5_AUTH_SETKEY) { + if (ret.code != 0) + errmsg = krb5_get_error_message(handle->context, ret.code); + + log_done("kadm5_setkey_principal", prime_arg, errmsg, &client_name, + &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + + free(prime_arg); +exit_func: + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + free_server_handle(handle); + return &ret; +} + chrand_ret * chrand_principal_2_svc(chrand_arg *arg, struct svc_req *rqstp) { diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 73f2811..3beb7cf 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -279,6 +279,12 @@ typedef struct _kadm5_config_params { int iprop_resync_timeout; } kadm5_config_params; +typedef struct _kadm5_key_data { + krb5_kvno kvno; + krb5_keyblock key; + krb5_keysalt salt; +} kadm5_key_data; + /* * functions */ @@ -403,6 +409,12 @@ kadm5_ret_t kadm5_setkey_principal_3(void *server_handle, krb5_keyblock *keyblocks, int n_keys); +kadm5_ret_t kadm5_setkey_principal_4(void *server_handle, + krb5_principal principal, + krb5_boolean keepold, + kadm5_key_data *key_data, + int n_key_data); + kadm5_ret_t kadm5_decrypt_key(void *server_handle, kadm5_principal_ent_t entry, krb5_int32 ktype, krb5_int32 stype, krb5_int32 diff --git a/src/lib/kadm5/admin_xdr.h b/src/lib/kadm5/admin_xdr.h index e46d542..cc44396 100644 --- a/src/lib/kadm5/admin_xdr.h +++ b/src/lib/kadm5/admin_xdr.h @@ -40,6 +40,7 @@ bool_t xdr_chpass3_arg(XDR *xdrs, chpass3_arg *objp); bool_t xdr_setv4key_arg(XDR *xdrs, setv4key_arg *objp); bool_t xdr_setkey_arg(XDR *xdrs, setkey_arg *objp); bool_t xdr_setkey3_arg(XDR *xdrs, setkey3_arg *objp); +bool_t xdr_setkey4_arg(XDR *xdrs, setkey4_arg *objp); bool_t xdr_chrand_arg(XDR *xdrs, chrand_arg *objp); bool_t xdr_chrand3_arg(XDR *xdrs, chrand3_arg *objp); bool_t xdr_chrand_ret(XDR *xdrs, chrand_ret *objp); @@ -68,3 +69,4 @@ bool_t xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp); bool_t xdr_krb5_key_data(XDR *xdrs, krb5_key_data *objp); bool_t xdr_krb5_string_attr(XDR *xdrs, krb5_string_attr *objp); bool_t xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp); +bool_t xdr_kadm5_key_data(XDR *xdrs, kadm5_key_data *objp); diff --git a/src/lib/kadm5/clnt/client_principal.c b/src/lib/kadm5/clnt/client_principal.c index 6af2a17..893d3c7 100644 --- a/src/lib/kadm5/clnt/client_principal.c +++ b/src/lib/kadm5/clnt/client_principal.c @@ -362,6 +362,33 @@ kadm5_setkey_principal_3(void *server_handle, } kadm5_ret_t +kadm5_setkey_principal_4(void *server_handle, + krb5_principal princ, + krb5_boolean keepold, + kadm5_key_data *key_data, + int n_key_data) +{ + setkey4_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.api_version = handle->api_version; + arg.princ = princ; + arg.keepold = keepold; + arg.key_data = key_data; + arg.n_key_data = n_key_data; + + if (princ == NULL || key_data == NULL || n_key_data == 0) + return EINVAL; + r = setkey_principal4_2(&arg, handle->clnt); + if (r == NULL) + eret(); + return r->code; +} + +kadm5_ret_t kadm5_randkey_principal_3(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, diff --git a/src/lib/kadm5/clnt/client_rpc.c b/src/lib/kadm5/clnt/client_rpc.c index 0d2f953..fa335a4 100644 --- a/src/lib/kadm5/clnt/client_rpc.c +++ b/src/lib/kadm5/clnt/client_rpc.c @@ -192,6 +192,21 @@ setkey_principal3_2(setkey3_arg *argp, CLIENT *clnt) return (&clnt_res); } +generic_ret * +setkey_principal4_2(setkey4_arg *argp, CLIENT *clnt) +{ + static generic_ret clnt_res; + + memset(&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, SETKEY_PRINCIPAL4, + (xdrproc_t)xdr_setkey4_arg, (caddr_t)argp, + (xdrproc_t)xdr_generic_ret, (caddr_t)&clnt_res, + TIMEOUT) != RPC_SUCCESS) { + return NULL; + } + return &clnt_res; +} + chrand_ret * chrand_principal_2(chrand_arg *argp, CLIENT *clnt) { diff --git a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports index b7d0ec5..d9e99ef 100644 --- a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports +++ b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports @@ -41,6 +41,7 @@ kadm5_rename_principal kadm5_set_string kadm5_setkey_principal kadm5_setkey_principal_3 +kadm5_setkey_principal_4 kadm5_setv4key_principal kadm5_unlock krb5_aprof_finish @@ -81,6 +82,7 @@ xdr_gprinc_arg xdr_gprinc_ret xdr_gprincs_arg xdr_gprincs_ret +xdr_kadm5_key_data xdr_kadm5_policy_ent_rec xdr_kadm5_principal_ent_rec xdr_kadm5_ret_t @@ -105,6 +107,7 @@ xdr_nullstring xdr_nulltype xdr_rprinc_arg xdr_setkey3_arg +xdr_setkey4_arg xdr_setkey_arg xdr_setv4key_arg xdr_ui_4 diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et index c717670..bc10a5b 100644 --- a/src/lib/kadm5/kadm_err.et +++ b/src/lib/kadm5/kadm_err.et @@ -63,4 +63,5 @@ 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" +error_code KADM5_SETKEY_BAD_KVNO, "Invalid multiple or duplicate kvnos in setkey operation" end diff --git a/src/lib/kadm5/kadm_rpc.h b/src/lib/kadm5/kadm_rpc.h index d5e9e7f..24565bd 100644 --- a/src/lib/kadm5/kadm_rpc.h +++ b/src/lib/kadm5/kadm_rpc.h @@ -108,6 +108,15 @@ struct setkey3_arg { }; typedef struct setkey3_arg setkey3_arg; +struct setkey4_arg { + krb5_ui_4 api_version; + krb5_principal princ; + krb5_boolean keepold; + kadm5_key_data *key_data; + int n_key_data; +}; +typedef struct setkey4_arg setkey4_arg; + struct chrand_arg { krb5_ui_4 api_version; krb5_principal princ; @@ -303,6 +312,9 @@ extern gstrings_ret * get_strings_2_svc(gstrings_arg *, struct svc_req *); #define SET_STRING 24 extern generic_ret * set_string_2(sstring_arg *, CLIENT *); extern generic_ret * set_string_2_svc(sstring_arg *, struct svc_req *); +#define SETKEY_PRINCIPAL4 25 +extern generic_ret * setkey_principal4_2(setkey4_arg *, CLIENT *); +extern generic_ret * setkey_principal4_2_svc(setkey4_arg *, struct svc_req *); extern bool_t xdr_cprinc_arg (); extern bool_t xdr_cprinc3_arg (); @@ -317,6 +329,7 @@ extern bool_t xdr_chpass3_arg (); extern bool_t xdr_setv4key_arg (); extern bool_t xdr_setkey_arg (); extern bool_t xdr_setkey3_arg (); +extern bool_t xdr_setkey4_arg (); extern bool_t xdr_chrand_arg (); extern bool_t xdr_chrand3_arg (); extern bool_t xdr_chrand_ret (); @@ -344,6 +357,7 @@ extern bool_t xdr_gstrings_arg (); extern bool_t xdr_gstrings_ret (); extern bool_t xdr_sstring_arg (); extern bool_t xdr_krb5_string_attr (); +extern bool_t xdr_kadm5_key_data (); #endif /* __KADM_RPC_H__ */ diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c index ba67084..bfd0341 100644 --- a/src/lib/kadm5/kadm_rpc_xdr.c +++ b/src/lib/kadm5/kadm_rpc_xdr.c @@ -772,6 +772,26 @@ xdr_setkey3_arg(XDR *xdrs, setkey3_arg *objp) } bool_t +xdr_setkey4_arg(XDR *xdrs, setkey4_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return FALSE; + } + if (!xdr_krb5_principal(xdrs, &objp->princ)) { + return FALSE; + } + if (!xdr_krb5_boolean(xdrs, &objp->keepold)) { + return FALSE; + } + if (!xdr_array(xdrs, (caddr_t *) &objp->key_data, + (unsigned int *) &objp->n_key_data, ~0, + sizeof(kadm5_key_data), xdr_kadm5_key_data)) { + return FALSE; + } + return TRUE; +} + +bool_t xdr_chrand_arg(XDR *xdrs, chrand_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { @@ -1157,3 +1177,18 @@ xdr_krb5_string_attr(XDR *xdrs, krb5_string_attr *objp) return FALSE; return TRUE; } + +bool_t +xdr_kadm5_key_data(XDR *xdrs, kadm5_key_data *objp) +{ + if (!xdr_krb5_kvno(xdrs, &objp->kvno)) + return FALSE; + if (!xdr_krb5_keyblock(xdrs, &objp->key)) + return FALSE; + if (!xdr_krb5_int16(xdrs, &objp->salt.type)) + return FALSE; + if (!xdr_bytes(xdrs, &objp->salt.data.data, + &objp->salt.data.length, ~0)) + return FALSE; + return TRUE; +} diff --git a/src/lib/kadm5/srv/libkadm5srv_mit.exports b/src/lib/kadm5/srv/libkadm5srv_mit.exports index 86af371..2cdd5a0 100644 --- a/src/lib/kadm5/srv/libkadm5srv_mit.exports +++ b/src/lib/kadm5/srv/libkadm5srv_mit.exports @@ -48,6 +48,7 @@ kadm5_rename_principal kadm5_set_string kadm5_setkey_principal kadm5_setkey_principal_3 +kadm5_setkey_principal_4 kadm5_setv4key_principal kadm5_unlock kdb_delete_entry @@ -131,6 +132,7 @@ xdr_osa_pw_hist_ent xdr_purgekeys_arg xdr_rprinc_arg xdr_setkey3_arg +xdr_setkey4_arg xdr_setkey_arg xdr_setv4key_arg xdr_sstring_arg diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c index f3791c6..4a083c2 100644 --- a/src/lib/kadm5/srv/svr_principal.c +++ b/src/lib/kadm5/srv/svr_principal.c @@ -2206,6 +2206,201 @@ done: return ret; } +/* Create a key/salt list from a key_data array. */ +static kadm5_ret_t +make_ks_from_key_data(krb5_context context, kadm5_key_data *key_data, + int n_key_data, krb5_key_salt_tuple **out) +{ + int i; + krb5_key_salt_tuple *ks; + + *out = NULL; + + ks = calloc(n_key_data, sizeof(*ks)); + if (ks == NULL) + return ENOMEM; + + for (i = 0; i < n_key_data; i++) { + ks[i].ks_enctype = key_data[i].key.enctype; + ks[i].ks_salttype = key_data[i].salt.type; + } + *out = ks; + return 0; +} + +kadm5_ret_t +kadm5_setkey_principal_4(void *server_handle, krb5_principal principal, + krb5_boolean keepold, kadm5_key_data *key_data, + int n_key_data) +{ + krb5_db_entry *kdb; + osa_princ_ent_rec adb; + krb5_int32 now; + kadm5_policy_ent_rec pol; + krb5_key_data *new_key_data = NULL; + int i, j, ret, n_new_key_data = 0; + krb5_kvno kvno; + krb5_boolean similar, have_pol = FALSE; + kadm5_server_handle_t handle = server_handle; + krb5_keyblock *act_mkey; + krb5_key_salt_tuple *ks_from_keys = NULL; + + CHECK_HANDLE(server_handle); + + krb5_clear_error_message(handle->context); + + if (principal == NULL || key_data == NULL || n_key_data == 0) + return EINVAL; + + /* hist_princ will be NULL when initializing the database. */ + if (hist_princ != NULL && + krb5_principal_compare(handle->context, principal, hist_princ)) + return KADM5_PROTECT_PRINCIPAL; + + /* For now, all keys must have the same kvno. */ + kvno = key_data[0].kvno; + for (i = 1; i < n_key_data; i++) { + if (key_data[i].kvno != kvno) + return KADM5_SETKEY_BAD_KVNO; + } + + ret = kdb_get_entry(handle, principal, &kdb, &adb); + if (ret) + return ret; + + if (kvno == 0) { + /* Pick the next kvno. */ + for (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; + } + kvno++; + } else if (keepold) { + /* Check that the kvno does collide with existing keys. */ + for (i = 0; i < kdb->n_key_data; i++) { + if (kdb->key_data[i].key_data_kvno == kvno) { + ret = KADM5_SETKEY_BAD_KVNO; + goto done; + } + } + } + + ret = make_ks_from_key_data(handle->context, key_data, n_key_data, + &ks_from_keys); + if (ret) + goto done; + + ret = apply_keysalt_policy(handle, adb.policy, n_key_data, ks_from_keys, + NULL, NULL); + free(ks_from_keys); + if (ret) + goto done; + + for (i = 0; i < n_key_data; i++) { + for (j = i + 1; j < n_key_data; j++) { + ret = krb5_c_enctype_compare(handle->context, + key_data[i].key.enctype, + key_data[j].key.enctype, + &similar); + if (ret) + goto done; + if (similar) { + if (key_data[i].salt.type == key_data[j].salt.type) { + ret = KADM5_SETKEY_DUP_ENCTYPES; + goto done; + } + } + } + } + + n_new_key_data = n_key_data + (keepold ? kdb->n_key_data : 0); + new_key_data = krb5_db_alloc(handle->context, NULL, + n_new_key_data * sizeof(krb5_key_data)); + if (new_key_data == NULL) { + n_new_key_data = 0; + ret = ENOMEM; + goto done; + } + memset(new_key_data, 0, n_new_key_data * sizeof(krb5_key_data)); + + n_new_key_data = 0; + for (i = 0; i < n_key_data; i++) { + + ret = kdb_get_active_mkey(handle, NULL, &act_mkey); + if (ret) + goto done; + + ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, + &key_data[i].key, &key_data[i].salt, + kvno, &new_key_data[i]); + if (ret) + goto done; + + n_new_key_data++; + } + + /* Copy old key data if necessary. */ + if (keepold) { + memcpy(new_key_data + n_new_key_data, kdb->key_data, + kdb->n_key_data * sizeof(krb5_key_data)); + memset(kdb->key_data, 0, kdb->n_key_data * sizeof(krb5_key_data)); + + /* + * Sort the keys to maintain the defined kvno order. We only need to + * sort if we keep old keys, as otherwise we allow only a single kvno + * to be specified. + */ + krb5_dbe_sort_key_data(new_key_data, n_new_key_data); + } + + /* Replace kdb->key_data with the new keys. */ + cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data); + kdb->key_data = new_key_data; + kdb->n_key_data = n_new_key_data; + new_key_data = NULL; + n_new_key_data = 0; + + kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; + + ret = krb5_timeofday(handle->context, &now); + if (ret) + goto done; + + if (adb.aux_attributes & KADM5_POLICY) { + ret = get_policy(handle, adb.policy, &pol, &have_pol); + if (ret) + goto done; + } + if (have_pol) { + if (pol.pw_max_life) + kdb->pw_expiration = now + pol.pw_max_life; + else + kdb->pw_expiration = 0; + } else { + kdb->pw_expiration = 0; + } + + ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now); + if (ret) + goto done; + + /* Unlock principal on this KDC. */ + kdb->fail_auth_count = 0; + + ret = kdb_put_entry(handle, kdb, &adb); + if (ret) + goto done; + + ret = KADM5_OK; + +done: + cleanup_key_data(handle->context, n_new_key_data, new_key_data); + kdb_free_entry(handle, kdb, &adb); + if (have_pol) + kadm5_free_policy_ent(handle->lhandle, &pol); + return ret; +} + /* * Return the list of keys like kadm5_randkey_principal, * but don't modify the principal. |