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