krb5 commit: Add kadm5_setkey_principal_4 RPC to kadmin
Greg Hudson
ghudson at mit.edu
Fri Feb 19 15:46:57 EST 2016
https://github.com/krb5/krb5/commit/1d4e83625f1c8cde7638702ab404f4594da3f062
commit 1d4e83625f1c8cde7638702ab404f4594da3f062
Author: Simo Sorce <simo at redhat.com>
Date: Tue Dec 15 14:49:22 2015 -0500
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)
src/kadmin/server/kadm_rpc_svc.c | 7 +
src/kadmin/server/server_stubs.c | 61 +++++++++
src/lib/kadm5/admin.h | 12 ++
src/lib/kadm5/admin_xdr.h | 2 +
src/lib/kadm5/clnt/client_principal.c | 27 ++++
src/lib/kadm5/clnt/client_rpc.c | 15 ++
src/lib/kadm5/clnt/libkadm5clnt_mit.exports | 3 +
src/lib/kadm5/kadm_err.et | 1 +
src/lib/kadm5/kadm_rpc.h | 14 ++
src/lib/kadm5/kadm_rpc_xdr.c | 35 +++++
src/lib/kadm5/srv/libkadm5srv_mit.exports | 2 +
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.
More information about the cvs-krb5
mailing list