krb5 commit: Add get_principal_keys RPC to kadmin
Greg Hudson
ghudson at mit.edu
Fri Feb 19 15:46:59 EST 2016
https://github.com/krb5/krb5/commit/8a64a49c3c836a2f4f03a0cbbdb89cfde9b29d1d
commit 8a64a49c3c836a2f4f03a0cbbdb89cfde9b29d1d
Author: Simo Sorce <simo at redhat.com>
Date: Thu Dec 17 17:46:16 2015 -0500
Add get_principal_keys RPC to kadmin
Change the prototype of kadm5_get_principal_keys() to report kvno and
salt information along with each key. Add an RPC for extracting keys,
requiring a new permission bit (which is not implied by 'x' or '*' in
kadm5.acl). Add kadm5_free_kadm5_key_data().
In kadmin, deconditionalize "kadmin ktadd -norandkey". Use the new
information from kadm5_get_principal_keys() to correctly set the kvno
for each key when existing keys are extracted, fixing issue #7852.
Add tests to t_keytab.py for the #7852 fix. Add tests to
lib/kadm5/unit-test for the get_principal_keys RPC.
[ghudson at mit.edu: factor out fetch_new_keys() from add_principal();
rewrite commit message to describe new RPC; add #7852 test cases;
squash with lib/kadm5/unit-test commit]
ticket: 8364 (new)
doc/admin/conf_files/kadm5_acl.rst | 3 +-
src/kadmin/cli/keytab.c | 111 ++++++++++++++++-----------
src/kadmin/server/kadm_rpc_svc.c | 7 ++
src/kadmin/server/server_stubs.c | 61 +++++++++++++++
src/kadmin/testing/scripts/init_db | 2 +-
src/lib/kadm5/admin.h | 14 ++--
src/lib/kadm5/admin_xdr.h | 2 +
src/lib/kadm5/clnt/Makefile.in | 2 +-
src/lib/kadm5/clnt/client_principal.c | 27 +++++++
src/lib/kadm5/clnt/client_rpc.c | 15 ++++
src/lib/kadm5/clnt/libkadm5clnt_mit.exports | 4 +
src/lib/kadm5/kadm_err.et | 1 +
src/lib/kadm5/kadm_rpc.h | 21 +++++-
src/lib/kadm5/kadm_rpc_xdr.c | 34 ++++++++
src/lib/kadm5/misc_free.c | 18 +++++
src/lib/kadm5/srv/Makefile.in | 2 +-
src/lib/kadm5/srv/libkadm5srv_mit.exports | 3 +
src/lib/kadm5/srv/server_acl.c | 1 +
src/lib/kadm5/srv/server_acl.h | 2 +-
src/lib/kadm5/srv/svr_principal.c | 40 +++++++---
src/lib/kadm5/unit-test/setkey-test.c | 26 ++++++
src/tests/t_keytab.py | 21 +++++-
src/util/k5test.py | 2 +-
23 files changed, 348 insertions(+), 71 deletions(-)
diff --git a/doc/admin/conf_files/kadm5_acl.rst b/doc/admin/conf_files/kadm5_acl.rst
index 2a6e634..f5cfd2f 100644
--- a/doc/admin/conf_files/kadm5_acl.rst
+++ b/doc/admin/conf_files/kadm5_acl.rst
@@ -47,12 +47,13 @@ ignored. Lines containing ACL entries have the format::
a [Dis]allows the addition of principals or policies
c [Dis]allows the changing of passwords for principals
d [Dis]allows the deletion of principals or policies
+ e [Dis]allows the extraction of principal keys
i [Dis]allows inquiries about principals or policies
l [Dis]allows the listing of all principals or policies
m [Dis]allows the modification of principals or policies
p [Dis]allows the propagation of the principal database (used in :ref:`incr_db_prop`)
s [Dis]allows the explicit setting of the key for a principal
- x Short for admcilsp. All privileges
+ x Short for admcilsp. All privileges (except ``e``)
\* Same as x.
== ======================================================
diff --git a/src/kadmin/cli/keytab.c b/src/kadmin/cli/keytab.c
index 96dc51b..b6edb75 100644
--- a/src/kadmin/cli/keytab.c
+++ b/src/kadmin/cli/keytab.c
@@ -52,13 +52,8 @@ static int norandkey;
static void
add_usage()
{
-#ifdef KADMIN_LOCAL
fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
"[-norandkey] [principal | -glob princ-exp] [...]\n"));
-#else
- fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
- "[principal | -glob princ-exp] [...]\n"));
-#endif
}
static void
@@ -140,13 +135,7 @@ kadmin_keytab_add(int argc, char **argv)
} else if (strcmp(*argv, "-q") == 0) {
quiet++;
} else if (strcmp(*argv, "-norandkey") == 0) {
-#ifdef KADMIN_LOCAL
norandkey++;
-#else
- fprintf(stderr,
- _("-norandkey option only valid for kadmin.local\n"));
- return;
-#endif
} else if (strcmp(*argv, "-e") == 0) {
argc--;
if (argc < 1) {
@@ -253,21 +242,71 @@ kadmin_keytab_remove(int argc, char **argv)
free(keytab_str);
}
+/* Generate new random keys for princ, and convert them into a kadm5_key_data
+ * array (with no salt information). */
+static krb5_error_code
+fetch_new_keys(void *lhandle, krb5_principal princ, krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ kadm5_key_data **key_data_out, int *nkeys_out)
+{
+ krb5_error_code code;
+ kadm5_key_data *key_data;
+ kadm5_principal_ent_rec princ_rec;
+ krb5_keyblock *keys = NULL;
+ int i, nkeys = 0;
+
+ *key_data_out = NULL;
+ *nkeys_out = 0;
+ memset(&princ_rec, 0, sizeof(princ_rec));
+
+ /* Generate new random keys. */
+ code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple,
+ &keys, &nkeys);
+ if (code)
+ goto cleanup;
+
+ /* Get the principal entry to find the kvno of the new keys. (This is not
+ * atomic, but randkey doesn't report the new kvno.) */
+ code = kadm5_get_principal(lhandle, princ, &princ_rec,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (code)
+ goto cleanup;
+
+ key_data = k5calloc(nkeys, sizeof(*key_data), &code);
+ if (key_data == NULL)
+ goto cleanup;
+
+ /* Transfer the keyblocks and free the container array. */
+ for (i = 0; i < nkeys; i++) {
+ key_data[i].key = keys[i];
+ key_data[i].kvno = princ_rec.kvno;
+ }
+ *key_data_out = key_data;
+ *nkeys_out = nkeys;
+ free(keys);
+ keys = NULL;
+ nkeys = 0;
+
+cleanup:
+ for (i = 0; i < nkeys; i++)
+ krb5_free_keyblock_contents(context, &keys[i]);
+ free(keys);
+ kadm5_free_principal_ent(lhandle, &princ_rec);
+ return code;
+}
+
static void
add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
krb5_boolean keepold, int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple, char *princ_str)
{
- kadm5_principal_ent_rec princ_rec;
krb5_principal princ = NULL;
krb5_keytab_entry new_entry;
- krb5_keyblock *keys;
+ kadm5_key_data *key_data;
int code, nkeys, i;
- memset(&princ_rec, 0, sizeof(princ_rec));
-
princ = NULL;
- keys = NULL;
+ key_data = NULL;
nkeys = 0;
code = krb5_parse_name(context, princ_str, &princ);
@@ -277,13 +316,13 @@ add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
goto cleanup;
}
-#ifdef KADMIN_LOCAL
- if (norandkey)
- code = kadm5_get_principal_keys(handle, princ, &keys, &nkeys);
- else
-#endif
- code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple,
- &keys, &nkeys);
+ if (norandkey) {
+ code = kadm5_get_principal_keys(handle, princ, 0, &key_data, &nkeys);
+ } else {
+ code = fetch_new_keys(handle, princ, keepold, n_ks_tuple, ks_tuple,
+ &key_data, &nkeys);
+ }
+
if (code != 0) {
if (code == KADM5_UNK_PRINC) {
fprintf(stderr, _("%s: Principal %s does not exist.\n"),
@@ -293,44 +332,28 @@ add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
goto cleanup;
}
- code = kadm5_get_principal(lhandle, princ, &princ_rec,
- KADM5_PRINCIPAL_NORMAL_MASK);
- if (code != 0) {
- com_err(whoami, code, _("while retrieving principal"));
- goto cleanup;
- }
-
for (i = 0; i < nkeys; i++) {
memset(&new_entry, 0, sizeof(new_entry));
new_entry.principal = princ;
- new_entry.key = keys[i];
- new_entry.vno = princ_rec.kvno;
+ new_entry.key = key_data[i].key;
+ new_entry.vno = key_data[i].kvno;
code = krb5_kt_add_entry(context, keytab, &new_entry);
if (code != 0) {
com_err(whoami, code, _("while adding key to keytab"));
- kadm5_free_principal_ent(lhandle, &princ_rec);
goto cleanup;
}
if (!quiet) {
printf(_("Entry for principal %s with kvno %d, "
"encryption type %s added to keytab %s.\n"),
- princ_str, princ_rec.kvno,
- etype_string(keys[i].enctype), keytab_str);
+ princ_str, key_data[i].kvno,
+ etype_string(key_data[i].key.enctype), keytab_str);
}
}
- code = kadm5_free_principal_ent(lhandle, &princ_rec);
- if (code != 0) {
- com_err(whoami, code, _("while freeing principal entry"));
- goto cleanup;
- }
-
cleanup:
- for (i = 0; i < nkeys; i++)
- krb5_free_keyblock_contents(context, &keys[i]);
- free(keys);
+ kadm5_free_kadm5_key_data(context, nkeys, key_data);
krb5_free_principal(context, princ);
}
diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c
index dee3938..e325164 100644
--- a/src/kadmin/server/kadm_rpc_svc.c
+++ b/src/kadmin/server/kadm_rpc_svc.c
@@ -59,6 +59,7 @@ void kadm_1(rqstp, transp)
chrand3_arg chrand_principal3_2_arg;
setkey3_arg setkey_principal3_2_arg;
setkey4_arg setkey_principal4_2_arg;
+ getpkeys_arg get_principal_keys_2_arg;
} argument;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
@@ -229,6 +230,12 @@ void kadm_1(rqstp, transp)
local = (char *(*)()) setkey_principal4_2_svc;
break;
+ case EXTRACT_KEYS:
+ xdr_argument = xdr_getpkeys_arg;
+ xdr_result = xdr_getpkeys_ret;
+ local = (char *(*)()) get_principal_keys_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 673cc2e..1126b7b 100644
--- a/src/kadmin/server/server_stubs.c
+++ b/src/kadmin/server/server_stubs.c
@@ -1877,3 +1877,64 @@ rqst2name(struct svc_req *rqstp)
else
return rqstp->rq_clntcred;
}
+
+getpkeys_ret *
+get_principal_keys_2_svc(getpkeys_arg *arg, struct svc_req *rqstp)
+{
+ static getpkeys_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_getpkeys_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_EXTRACT, arg->princ, NULL)) {
+ ret.code = kadm5_get_principal_keys((void *)handle, arg->princ,
+ arg->kvno, &ret.key_data,
+ &ret.n_key_data);
+ } else {
+ log_unauth("kadm5_get_principal_keys", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret.code = KADM5_AUTH_EXTRACT;
+ }
+
+ if (ret.code != KADM5_AUTH_EXTRACT) {
+ if (ret.code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret.code);
+
+ log_done("kadm5_get_principal_keys", 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;
+}
diff --git a/src/kadmin/testing/scripts/init_db b/src/kadmin/testing/scripts/init_db
index 06b3384..cd71656 100755
--- a/src/kadmin/testing/scripts/init_db
+++ b/src/kadmin/testing/scripts/init_db
@@ -200,7 +200,7 @@ if [ $? -ne 0 ]; then
fi
cat > $K5ROOT/ovsec_adm.acl <<EOF
-admin@$REALM admcils
+admin@$REALM admcilse
admin/get@$REALM il
admin/modify@$REALM mc
admin/delete@$REALM d
diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h
index 3beb7cf..14e4865 100644
--- a/src/lib/kadm5/admin.h
+++ b/src/lib/kadm5/admin.h
@@ -467,16 +467,11 @@ krb5_error_code kadm5_init_krb5_context (krb5_context *);
krb5_error_code kadm5_init_iprop(void *server_handle, char **db_args);
-/*
- * kadm5_get_principal_keys is used only by kadmin.local to extract existing
- * keys from the database without changing them. It should never be exposed
- * to the network protocol.
- */
kadm5_ret_t kadm5_get_principal_keys(void *server_handle,
krb5_principal principal,
- krb5_keyblock **keyblocks,
- int *n_keys);
-
+ krb5_kvno kvno,
+ kadm5_key_data **key_data,
+ int *n_key_data);
kadm5_ret_t kadm5_purgekeys(void *server_handle,
krb5_principal principal,
@@ -496,6 +491,9 @@ kadm5_ret_t kadm5_free_strings(void *server_handle,
krb5_string_attr *strings,
int count);
+kadm5_ret_t kadm5_free_kadm5_key_data(krb5_context context, int n_key_data,
+ kadm5_key_data *key_data);
+
KADM5INT_END_DECLS
#endif /* __KADM5_ADMIN_H__ */
diff --git a/src/lib/kadm5/admin_xdr.h b/src/lib/kadm5/admin_xdr.h
index cc44396..2d22611 100644
--- a/src/lib/kadm5/admin_xdr.h
+++ b/src/lib/kadm5/admin_xdr.h
@@ -70,3 +70,5 @@ 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);
+bool_t xdr_getpkeys_arg(XDR *xdrs, getpkeys_arg *objp);
+bool_t xdr_getpkeys_ret(XDR *xdrs, getpkeys_ret *objp);
diff --git a/src/lib/kadm5/clnt/Makefile.in b/src/lib/kadm5/clnt/Makefile.in
index 9301241..1c83ee0 100644
--- a/src/lib/kadm5/clnt/Makefile.in
+++ b/src/lib/kadm5/clnt/Makefile.in
@@ -3,7 +3,7 @@ BUILDTOP=$(REL)..$(S)..$(S)..
LOCALINCLUDES = -I$(BUILDTOP)/include/kadm5
LIBBASE=kadm5clnt_mit
-LIBMAJOR=10
+LIBMAJOR=11
LIBMINOR=0
STOBJLISTS=../OBJS.ST OBJS.ST
SHLIB_EXPDEPS=\
diff --git a/src/lib/kadm5/clnt/client_principal.c b/src/lib/kadm5/clnt/client_principal.c
index 893d3c7..5718e17 100644
--- a/src/lib/kadm5/clnt/client_principal.c
+++ b/src/lib/kadm5/clnt/client_principal.c
@@ -557,3 +557,30 @@ kadm5_set_string(void *server_handle, krb5_principal principal,
eret();
return r->code;
}
+
+kadm5_ret_t
+kadm5_get_principal_keys(void *server_handle, krb5_principal princ,
+ krb5_kvno kvno, kadm5_key_data **key_data,
+ int *n_key_data)
+{
+ getpkeys_arg arg;
+ getpkeys_ret *r;
+ kadm5_server_handle_t handle = server_handle;
+
+ CHECK_HANDLE(server_handle);
+
+ arg.api_version = handle->api_version;
+ arg.princ = princ;
+ arg.kvno = kvno;
+
+ if (princ == NULL || key_data == NULL || n_key_data == 0)
+ return EINVAL;
+ r = get_principal_keys_2(&arg, handle->clnt);
+ if (r == NULL)
+ eret();
+ if (r->code == 0) {
+ *key_data = r->key_data;
+ *n_key_data = r->n_key_data;
+ }
+ return r->code;
+}
diff --git a/src/lib/kadm5/clnt/client_rpc.c b/src/lib/kadm5/clnt/client_rpc.c
index fa335a4..a5f74e6 100644
--- a/src/lib/kadm5/clnt/client_rpc.c
+++ b/src/lib/kadm5/clnt/client_rpc.c
@@ -386,3 +386,18 @@ set_string_2(sstring_arg *argp, CLIENT *clnt)
}
return (&clnt_res);
}
+
+getpkeys_ret *
+get_principal_keys_2(getpkeys_arg *argp, CLIENT *clnt)
+{
+ static getpkeys_ret clnt_res;
+
+ memset(&clnt_res, 0, sizeof(clnt_res));
+ if (clnt_call(clnt, EXTRACT_KEYS,
+ (xdrproc_t)xdr_getpkeys_arg, (caddr_t)argp,
+ (xdrproc_t)xdr_getpkeys_ret, (caddr_t)&clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return NULL;
+ }
+ return &clnt_res;
+}
diff --git a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports
index d9e99ef..9d1a573 100644
--- a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports
+++ b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports
@@ -12,6 +12,7 @@ kadm5_delete_principal
kadm5_destroy
kadm5_flush
kadm5_free_config_params
+kadm5_free_kadm5_key_data
kadm5_free_key_data
kadm5_free_name_list
kadm5_free_policy_ent
@@ -22,6 +23,7 @@ kadm5_get_config_params
kadm5_get_policies
kadm5_get_policy
kadm5_get_principal
+kadm5_get_principal_keys
kadm5_get_principals
kadm5_get_privs
kadm5_get_strings
@@ -73,6 +75,8 @@ xdr_cprinc_arg
xdr_dpol_arg
xdr_dprinc_arg
xdr_generic_ret
+xdr_getpkeys_arg
+xdr_getpkeys_ret
xdr_getprivs_ret
xdr_gpol_arg
xdr_gpol_ret
diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et
index bc10a5b..56a4e27 100644
--- a/src/lib/kadm5/kadm_err.et
+++ b/src/lib/kadm5/kadm_err.et
@@ -64,4 +64,5 @@ error_code KADM5_CANT_RESOLVE, "Cannot resolve network address for admin server
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"
+error_code KADM5_AUTH_EXTRACT, "Operation requires ``extract-keys'' privilege"
end
diff --git a/src/lib/kadm5/kadm_rpc.h b/src/lib/kadm5/kadm_rpc.h
index 24565bd..a75df11 100644
--- a/src/lib/kadm5/kadm_rpc.h
+++ b/src/lib/kadm5/kadm_rpc.h
@@ -238,6 +238,21 @@ struct sstring_arg {
};
typedef struct sstring_arg sstring_arg;
+struct getpkeys_arg {
+ krb5_ui_4 api_version;
+ krb5_principal princ;
+ krb5_kvno kvno;
+};
+typedef struct getpkeys_arg getpkeys_arg;
+
+struct getpkeys_ret {
+ krb5_ui_4 api_version;
+ kadm5_ret_t code;
+ kadm5_key_data *key_data;
+ int n_key_data;
+};
+typedef struct getpkeys_ret getpkeys_ret;
+
#define KADM 2112
#define KADMVERS 2
#define CREATE_PRINCIPAL 1
@@ -315,6 +330,9 @@ 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 *);
+#define EXTRACT_KEYS 26
+extern getpkeys_ret * get_principal_keys_2(getpkeys_arg *, CLIENT *);
+extern getpkeys_ret * get_principal_keys_2_svc(getpkeys_arg *, struct svc_req *);
extern bool_t xdr_cprinc_arg ();
extern bool_t xdr_cprinc3_arg ();
@@ -358,6 +376,7 @@ 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 ();
-
+extern bool_t xdr_getpkeys_arg ();
+extern bool_t xdr_getpkeys_ret ();
#endif /* __KADM_RPC_H__ */
diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c
index bfd0341..2892d41 100644
--- a/src/lib/kadm5/kadm_rpc_xdr.c
+++ b/src/lib/kadm5/kadm_rpc_xdr.c
@@ -1192,3 +1192,37 @@ xdr_kadm5_key_data(XDR *xdrs, kadm5_key_data *objp)
return FALSE;
return TRUE;
}
+
+bool_t
+xdr_getpkeys_arg(XDR *xdrs, getpkeys_arg *objp)
+{
+ if (!xdr_ui_4(xdrs, &objp->api_version)) {
+ return FALSE;
+ }
+ if (!xdr_krb5_principal(xdrs, &objp->princ)) {
+ return FALSE;
+ }
+ if (!xdr_krb5_kvno(xdrs, &objp->kvno)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool_t
+xdr_getpkeys_ret(XDR *xdrs, getpkeys_ret *objp)
+{
+ if (!xdr_ui_4(xdrs, &objp->api_version)) {
+ return FALSE;
+ }
+ if (!xdr_kadm5_ret_t(xdrs, &objp->code)) {
+ return FALSE;
+ }
+ if (objp->code == KADM5_OK) {
+ 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;
+}
diff --git a/src/lib/kadm5/misc_free.c b/src/lib/kadm5/misc_free.c
index 497b8c2..74d2376 100644
--- a/src/lib/kadm5/misc_free.c
+++ b/src/lib/kadm5/misc_free.c
@@ -119,3 +119,21 @@ kadm5_free_strings(void *server_handle, krb5_string_attr *strings,
free(strings);
return KADM5_OK;
}
+
+kadm5_ret_t
+kadm5_free_kadm5_key_data(krb5_context context, int n_key_data,
+ kadm5_key_data *key_data)
+{
+ int i;
+
+ if (key_data == NULL)
+ return KADM5_OK;
+
+ for (i = 0; i < n_key_data; i++) {
+ krb5_free_keyblock_contents(context, &key_data[i].key);
+ krb5_free_data_contents(context, &key_data[i].salt.data);
+ }
+ free(key_data);
+
+ return KADM5_OK;
+}
diff --git a/src/lib/kadm5/srv/Makefile.in b/src/lib/kadm5/srv/Makefile.in
index 3fd4b9e..6aa7d10 100644
--- a/src/lib/kadm5/srv/Makefile.in
+++ b/src/lib/kadm5/srv/Makefile.in
@@ -9,7 +9,7 @@ DEFINES = @HESIOD_DEFS@
##DOSLIBNAME = libkadm5srv.lib
LIBBASE=kadm5srv_mit
-LIBMAJOR=10
+LIBMAJOR=11
LIBMINOR=0
STOBJLISTS=../OBJS.ST OBJS.ST
diff --git a/src/lib/kadm5/srv/libkadm5srv_mit.exports b/src/lib/kadm5/srv/libkadm5srv_mit.exports
index 2cdd5a0..aedfdd7 100644
--- a/src/lib/kadm5/srv/libkadm5srv_mit.exports
+++ b/src/lib/kadm5/srv/libkadm5srv_mit.exports
@@ -19,6 +19,7 @@ kadm5_delete_principal
kadm5_destroy
kadm5_flush
kadm5_free_config_params
+kadm5_free_kadm5_key_data
kadm5_free_key_data
kadm5_free_name_list
kadm5_free_policy_ent
@@ -92,6 +93,8 @@ xdr_cprinc_arg
xdr_dpol_arg
xdr_dprinc_arg
xdr_generic_ret
+xdr_getpkeys_arg
+xdr_getpkeys_ret
xdr_getprivs_ret
xdr_gpol_arg
xdr_gpol_ret
diff --git a/src/lib/kadm5/srv/server_acl.c b/src/lib/kadm5/srv/server_acl.c
index 97b2f7e..59ed0b9 100644
--- a/src/lib/kadm5/srv/server_acl.c
+++ b/src/lib/kadm5/srv/server_acl.c
@@ -65,6 +65,7 @@ static const aop_t acl_op_table[] = {
{ 's', ACL_SETKEY },
{ 'x', ACL_ALL_MASK },
{ '*', ACL_ALL_MASK },
+ { 'e', ACL_EXTRACT },
{ '\0', 0 }
};
diff --git a/src/lib/kadm5/srv/server_acl.h b/src/lib/kadm5/srv/server_acl.h
index d492ea9..d8db2f7 100644
--- a/src/lib/kadm5/srv/server_acl.h
+++ b/src/lib/kadm5/srv/server_acl.h
@@ -54,7 +54,7 @@
#define ACL_CHANGEPW 8
/* #define ACL_CHANGE_OWN_PW 16 */
#define ACL_INQUIRE 32
-/* #define ACL_EXTRACT 64 */
+#define ACL_EXTRACT 64
#define ACL_LIST 128
#define ACL_SETKEY 256
#define ACL_IPROP 512
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
index cc1be5a..800e8db 100644
--- a/src/lib/kadm5/srv/svr_principal.c
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -2203,36 +2203,54 @@ done:
kadm5_ret_t
kadm5_get_principal_keys(void *server_handle /* IN */,
krb5_principal principal /* IN */,
- krb5_keyblock **keyblocks /* OUT */,
- int *n_keys /* OUT */)
+ krb5_kvno kvno /* IN */,
+ kadm5_key_data **key_data_out /* OUT */,
+ int *n_key_data_out /* OUT */)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
kadm5_ret_t ret;
kadm5_server_handle_t handle = server_handle;
+ kadm5_key_data *key_data = NULL;
+ int i, nkeys = 0;
- if (keyblocks)
- *keyblocks = NULL;
+ if (principal == NULL || key_data_out == NULL || n_key_data_out == NULL)
+ return EINVAL;
CHECK_HANDLE(server_handle);
- if (principal == NULL)
- return EINVAL;
-
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
- if (keyblocks) {
- ret = decrypt_key_data(handle->context,
- kdb->n_key_data, kdb->key_data,
- keyblocks, n_keys);
+ key_data = calloc(kdb->n_key_data, sizeof(kadm5_key_data));
+ if (key_data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0, nkeys = 0; i < kdb->n_key_data; i++) {
+ if (kvno != 0 && kvno != kdb->key_data[i].key_data_kvno)
+ continue;
+ key_data[nkeys].kvno = kdb->key_data[i].key_data_kvno;
+
+ ret = krb5_dbe_decrypt_key_data(handle->context, NULL,
+ &kdb->key_data[i],
+ &key_data[nkeys].key,
+ &key_data[nkeys].salt);
if (ret)
goto done;
+ nkeys++;
}
+ *n_key_data_out = nkeys;
+ *key_data_out = key_data;
+ key_data = NULL;
+ nkeys = 0;
ret = KADM5_OK;
+
done:
kdb_free_entry(handle, kdb, &adb);
+ kadm5_free_kadm5_key_data(handle->context, nkeys, key_data);
return ret;
}
diff --git a/src/lib/kadm5/unit-test/setkey-test.c b/src/lib/kadm5/unit-test/setkey-test.c
index 2ecb5eb..0729885 100644
--- a/src/lib/kadm5/unit-test/setkey-test.c
+++ b/src/lib/kadm5/unit-test/setkey-test.c
@@ -137,6 +137,8 @@ main(int argc, char **argv)
*/
for (test = 0; tests[test] != NULL; test++) {
krb5_keyblock *testp = tests[test];
+ kadm5_key_data *extracted;
+ int n_extracted, match;
printf("+ Test %d:\n", test);
for (encnum = 0; testp[encnum].magic != -1; encnum++) {
@@ -165,9 +167,31 @@ main(int argc, char **argv)
exit(1);
}
+ ret = kadm5_get_principal_keys(handle, princ, 0, &extracted,
+ &n_extracted);
+ if (ret) {
+ com_err(whoami, ret, "while extracting keys");
+ exit(1);
+ }
+
for (encnum = 0; testp[encnum].magic != -1; encnum++) {
printf("+ enctype %d\n", testp[encnum].enctype);
+ for (match = 0; match < n_extracted; match++) {
+ if (extracted[match].key.enctype == testp[encnum].enctype)
+ break;
+ }
+ if (match >= n_extracted) {
+ com_err(whoami, KRB5_WRONG_ETYPE, "while matching enctypes");
+ exit(1);
+ }
+ if (extracted[match].key.length != testp[encnum].length ||
+ memcmp(extracted[match].key.contents, testp[encnum].contents,
+ testp[encnum].length) != 0) {
+ com_err(whoami, KRB5_KDB_NO_MATCHING_KEY, "verifying keys");
+ exit(1);
+ }
+
memset(&ktent, 0, sizeof(ktent));
ktent.principal = princ;
ktent.key = testp[encnum];
@@ -206,6 +230,8 @@ main(int argc, char **argv)
exit(1);
}
}
+
+ (void)kadm5_free_kadm5_key_data(context, n_extracted, extracted);
}
ret = krb5_kt_close(context, kt);
diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py
index 6b7e9bc..a06e6c2 100755
--- a/src/tests/t_keytab.py
+++ b/src/tests/t_keytab.py
@@ -41,6 +41,26 @@ if 'keytab specified, forcing -k' not in output:
fail('Expected output not seen from kinit -i')
realm.klist(realm.user_princ)
+# Test extracting keys with multiple key versions present.
+os.remove(realm.keytab)
+realm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.host_princ])
+out = realm.run([kadminl, 'ktadd', '-norandkey', realm.host_princ])
+if 'with kvno 1,' not in out or 'with kvno 2,' not in out:
+ fail('Expected output not seen from kadmin.local ktadd -norandkey')
+out = realm.run([klist, '-k', '-e'])
+if ' 1 host/' not in out or ' 2 host/' not in out:
+ fail('Expected output not seen from klist -k -e')
+
+# Test again using kadmin over the network.
+realm.prep_kadmin()
+os.remove(realm.keytab)
+out = realm.run_kadmin(['ktadd', '-norandkey', realm.host_princ])
+if 'with kvno 1,' not in out or 'with kvno 2,' not in out:
+ fail('Expected output not seen from kadmin.local ktadd -norandkey')
+out = realm.run([klist, '-k', '-e'])
+if ' 1 host/' not in out or ' 2 host/' not in out:
+ fail('Expected output not seen from klist -k -e')
+
# Test handling of kvno values beyond 255. Use kadmin over the
# network since we used to have an 8-bit limit on kvno marshalling.
@@ -57,7 +77,6 @@ def test_key_rotate(realm, princ, expected_kvno):
if ('Key: vno %d,' % expected_kvno) not in out:
fail('vno %d not seen in getprinc output' % expected_kvno)
-realm.prep_kadmin()
princ = 'foo/bar@%s' % realm.realm
realm.addprinc(princ)
os.remove(realm.keytab)
diff --git a/src/util/k5test.py b/src/util/k5test.py
index df6a68a..c1df32f 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -851,7 +851,7 @@ class K5Realm(object):
global hostname
filename = os.path.join(self.testdir, 'acl')
file = open(filename, 'w')
- file.write('%s *\n' % self.admin_princ)
+ file.write('%s *e\n' % self.admin_princ)
file.write('kiprop/%s@%s p\n' % (hostname, self.realm))
file.close()
More information about the cvs-krb5
mailing list