Add client-side stubs and functions with additional capabilities to
authorTom Yu <tlyu@mit.edu>
Mon, 14 Feb 2000 00:07:10 +0000 (00:07 +0000)
committerTom Yu <tlyu@mit.edu>
Mon, 14 Feb 2000 00:07:10 +0000 (00:07 +0000)
take key_salt_tuples and optionally keep old keys around.  Add
server-side functionality for setkey with key_salt_tuple and "keepold"
functionality.  Update rpc stubs and xdr functions/headers
appropriately.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@12040 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/kadm5/ChangeLog
src/lib/kadm5/admin.h
src/lib/kadm5/clnt/ChangeLog
src/lib/kadm5/clnt/client_principal.c
src/lib/kadm5/clnt/client_rpc.c
src/lib/kadm5/kadm_err.et
src/lib/kadm5/kadm_rpc.h
src/lib/kadm5/kadm_rpc_xdr.c
src/lib/kadm5/srv/ChangeLog
src/lib/kadm5/srv/svr_principal.c

index 2eb05a8bcf7ce10e553add651d9b949a15acfb5c..a951c04dc042a0d96e7f18e37196e89d4f77fcac 100644 (file)
@@ -1,3 +1,14 @@
+2000-02-13  Tom Yu  <tlyu@mit.edu>
+
+       * kadm_rpc_xdr.c: Add xdr functions for new kadm rpc functions.
+
+       * kadm_rpc.h: Add arg structs, prototypes, constants for new kadm
+       rpc functions.
+
+       * kadm_err.et: Add error code KADM5_SETKEY3_ETYPE_MISMATCH.
+
+       * admin.h: Add prototype for setkey_principal_3.
+
 1999-12-01  Ken Raeburn  <raeburn@mit.edu>
 
        * logger.c (klog_vsyslog): Convert pid_t to long for printing.
index 6aa432f34c6b37974e48f98999e79c0fad4eb70e..d8904e664ab3322abf80ec4f62a98e12ef567632 100644 (file)
@@ -361,6 +361,14 @@ kadm5_ret_t    kadm5_setkey_principal(void *server_handle,
                                      krb5_keyblock *keyblocks,
                                      int n_keys);
 
+kadm5_ret_t    kadm5_setkey_principal_3(void *server_handle,
+                                       krb5_principal principal,
+                                       krb5_boolean keepold,
+                                       int n_ks_tuple,
+                                       krb5_key_salt_tuple *ks_tuple,
+                                       krb5_keyblock *keyblocks,
+                                       int n_keys);
+
 kadm5_ret_t    kadm5_create_policy(void *server_handle,
                                   kadm5_policy_ent_t ent,
                                   long mask);
index de4c52557a639e59afda69d138e413e0b145b10a..37f4207edd8c2785164f8adaa7fa5189403c24fa 100644 (file)
@@ -1,3 +1,10 @@
+2000-02-13  Tom Yu  <tlyu@mit.edu>
+
+       * client_rpc.c: Add new client stubs.
+
+       * client_principal.c: Add new functions for client-side kadm rpc
+       calls.
+
 2000-01-27  Ken Raeburn  <raeburn@raeburn.org>
 
        * client_init.c (enctypes): New array, listing only
index b23ff1f2e657d5c9afd2684a74ffeb9866f9544a..97ea6989c7b61a32581e0e471b1138c92c0736b6 100644 (file)
@@ -71,6 +71,68 @@ kadm5_create_principal(void *server_handle,
     return r->code;
 }
 
+kadm5_ret_t
+kadm5_create_principal_3(void *server_handle,
+                        kadm5_principal_ent_t princ, long mask,
+                        krb5_boolean keepold, int n_ks_tuple,
+                        krb5_key_salt_tuple *ks_tuple,
+                        char *pw)
+{
+    generic_ret                *r;
+    cprinc3_arg                arg;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    memset(&arg, 0, sizeof(arg));
+    arg.mask = mask;
+    arg.passwd = pw;
+    arg.api_version = handle->api_version;
+    arg.keepold = keepold;
+    arg.n_ks_tuple = n_ks_tuple;
+    arg.ks_tuple = ks_tuple;
+
+    if(princ == NULL)
+       return EINVAL;
+
+    if (handle->api_version == KADM5_API_VERSION_1) {
+       memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec_v1));
+    } else {
+       memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec));
+    }
+    if (handle->api_version == KADM5_API_VERSION_1) {
+        /*
+         * hack hack cough cough.
+         * krb5_unparse name dumps core if we pass it in garbage
+         * or null. So, since the client is not allowed to set mod_name
+         * anyway, we just fill it in with a dummy principal. The server of
+         * course ignores this.
+         */
+        krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name);
+    } else
+        arg.rec.mod_name = NULL;
+    
+    if(!(mask & KADM5_POLICY))
+       arg.rec.policy = NULL;
+    if (! (mask & KADM5_KEY_DATA)) {
+        arg.rec.n_key_data = 0;
+        arg.rec.key_data = NULL;
+    }
+    if (! (mask & KADM5_TL_DATA)) {
+        arg.rec.n_tl_data = 0;
+        arg.rec.tl_data = NULL;
+    }
+        
+    r = create_principal3_1(&arg, handle->clnt);
+
+    if (handle->api_version == KADM5_API_VERSION_1)
+        krb5_free_principal(handle->context, arg.rec.mod_name);
+
+    if(r == NULL)
+       return KADM5_RPC_ERROR;
+    return r->code;
+}
+
 kadm5_ret_t
 kadm5_delete_principal(void *server_handle, krb5_principal principal)
 {
@@ -261,6 +323,33 @@ kadm5_chpass_principal(void *server_handle,
     return r->code;
 }
 
+kadm5_ret_t
+kadm5_chpass_principal_3(void *server_handle,
+                        krb5_principal princ, krb5_boolean keepold,
+                        int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+                        char *password)
+{
+    chpass3_arg                arg;
+    generic_ret                *r;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    arg.princ = princ;
+    arg.pass = password;
+    arg.api_version = handle->api_version;
+    arg.keepold = keepold;
+    arg.n_ks_tuple = n_ks_tuple;
+    arg.ks_tuple = ks_tuple;
+
+    if(princ == NULL)
+       return EINVAL;
+    r = chpass_principal3_1(&arg, handle->clnt);
+    if(r == NULL)
+       return KADM5_RPC_ERROR;        
+    return r->code;
+}
+
 kadm5_ret_t
 kadm5_setv4key_principal(void *server_handle,
                         krb5_principal princ,
@@ -309,6 +398,90 @@ kadm5_setkey_principal(void *server_handle,
     return r->code;
 }
 
+kadm5_ret_t
+kadm5_setkey_principal_3(void *server_handle,
+                        krb5_principal princ,
+                        krb5_boolean keepold, int n_ks_tuple,
+                        krb5_key_salt_tuple *ks_tuple,
+                        krb5_keyblock *keyblocks,
+                        int n_keys)
+{
+    setkey3_arg                arg;
+    generic_ret                *r;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    arg.princ = princ;
+    arg.keyblocks = keyblocks;
+    arg.n_keys = n_keys;
+    arg.api_version = handle->api_version;
+    arg.keepold = keepold;
+    arg.n_ks_tuple = n_ks_tuple;
+    arg.ks_tuple = ks_tuple;
+
+    if(princ == NULL || keyblocks == NULL)
+       return EINVAL;
+    r = setkey_principal3_1(&arg, handle->clnt);
+    if(r == NULL)
+       return KADM5_RPC_ERROR;        
+    return r->code;
+}
+
+kadm5_ret_t
+kadm5_randkey_principal_3(void *server_handle,
+                         krb5_principal princ,
+                         krb5_boolean keepold, int n_ks_tuple,
+                         krb5_key_salt_tuple *ks_tuple,
+                         krb5_keyblock **key, int *n_keys)
+{
+    chrand3_arg                arg;
+    chrand_ret         *r;
+    krb5_keyblock      new;
+    kadm5_server_handle_t handle = server_handle;
+    int                        i, ret;
+
+    CHECK_HANDLE(server_handle);
+
+    arg.princ = princ;
+    arg.api_version = handle->api_version;
+    arg.keepold = keepold;
+    arg.n_ks_tuple = n_ks_tuple;
+    arg.ks_tuple = ks_tuple;
+
+    if(princ == NULL)
+       return EINVAL;
+    r = chrand_principal3_1(&arg, handle->clnt);
+    if(r == NULL)
+       return KADM5_RPC_ERROR;
+    if (handle->api_version == KADM5_API_VERSION_1) {
+        if (key)
+             krb5_copy_keyblock(handle->context, &r->key, key);
+    } else {
+        if (n_keys)
+             *n_keys = r->n_keys;
+        if (key) {
+             if(r->n_keys) {
+                     *key = (krb5_keyblock *) 
+                             malloc(r->n_keys*sizeof(krb5_keyblock));
+                     if (*key == NULL)
+                             return ENOMEM;
+                     for (i = 0; i < r->n_keys; i++) {
+                             ret = krb5_copy_keyblock_contents(handle->context,
+                                                               &r->keys[i],
+                                                               &(*key)[i]);
+                             if (ret) {
+                                     free(*key);
+                                     return ENOMEM;
+                             }
+                     }
+             } else *key = NULL;
+         }
+    }
+
+    return r->code;
+}
+
 kadm5_ret_t
 kadm5_randkey_principal(void *server_handle,
                        krb5_principal princ,
index 8a284e38200eb803bf0ef69f0b49e6569ef3efd7..7c55ab46102f367bfd2c781e137059871b84a5dd 100644 (file)
@@ -21,6 +21,20 @@ create_principal_1(argp, clnt)
        return (&res);
 }
 
+generic_ret *
+create_principal3_1(argp, clnt)
+       cprinc_arg *argp;
+       CLIENT *clnt;
+{
+       static generic_ret res;
+
+       memset((char *)&res, 0, sizeof(res));
+       if (clnt_call(clnt, CREATE_PRINCIPAL3, xdr_cprinc3_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&res);
+}
+
 generic_ret *
 delete_principal_1(argp, clnt)
        dprinc_arg *argp;
@@ -106,6 +120,20 @@ chpass_principal_1(argp, clnt)
        return (&res);
 }
 
+generic_ret *
+chpass_principal3_1(argp, clnt)
+       chpass_arg *argp;
+       CLIENT *clnt;
+{
+       static generic_ret res;
+
+       memset((char *)&res, 0, sizeof(res));
+       if (clnt_call(clnt, CHPASS_PRINCIPAL3, xdr_chpass3_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&res);
+}
+
 generic_ret *
 setv4key_principal_1(argp, clnt)
        setv4key_arg *argp;
@@ -134,6 +162,20 @@ setkey_principal_1(argp, clnt)
        return (&res);
 }
 
+generic_ret *
+setkey_principal3_1(argp, clnt)
+       setkey_arg *argp;
+       CLIENT *clnt;
+{
+       static generic_ret res;
+
+       memset((char *)&res, 0, sizeof(res));
+       if (clnt_call(clnt, SETKEY_PRINCIPAL3, xdr_setkey3_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&res);
+}
+
 chrand_ret *
 chrand_principal_1(argp, clnt)
        chrand_arg *argp;
@@ -148,6 +190,20 @@ chrand_principal_1(argp, clnt)
        return (&res);
 }
 
+chrand_ret *
+chrand_principal3_1(argp, clnt)
+       chrand_arg *argp;
+       CLIENT *clnt;
+{
+       static chrand_ret res;
+
+       memset((char *)&res, 0, sizeof(res));
+       if (clnt_call(clnt, CHRAND_PRINCIPAL3, xdr_chrand3_arg, argp, xdr_chrand_ret, &res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&res);
+}
+
 generic_ret *
 create_policy_1(argp, clnt)
        cpol_arg *argp;
index 29c65818c181061379d244d843057397ac6bd6e3..0e2b2c523e0f5fee381a4ed8f4e1300b31df7624 100644 (file)
@@ -58,3 +58,4 @@ error_code KADM5_AUTH_SETKEY, "Operation requires ``set-key'' privilege"
 error_code KADM5_SETKEY_DUP_ENCTYPES, "Multiple values for single or folded enctype"
 error_code KADM5_SETV4KEY_INVAL_ENCTYPE, "Invalid enctype for setv4key"
 end
+error_code KADM5_SETKEY3_ETYPE_MISMATCH, "Mismatched enctypes for setkey3"
index 468e072b244727c6185cd3e033ab12a42cbc95db..e1aa364321b903c2a2b4b93642494f586225720b 100644 (file)
@@ -12,6 +12,18 @@ struct cprinc_arg {
 typedef struct cprinc_arg cprinc_arg;
 bool_t xdr_cprinc_arg();
 
+struct cprinc3_arg {
+       krb5_ui_4 api_version;
+       kadm5_principal_ent_rec rec;
+       long mask;
+       krb5_boolean keepold;
+       int n_ks_tuple;
+       krb5_key_salt_tuple *ks_tuple;
+       char *passwd;
+};
+typedef struct cprinc3_arg cprinc3_arg;
+bool_t xdr_cprinc3_arg();
+
 struct generic_ret {
        krb5_ui_4 api_version;
        kadm5_ret_t code;
@@ -66,6 +78,17 @@ struct chpass_arg {
 typedef struct chpass_arg chpass_arg;
 bool_t xdr_chpass_arg();
 
+struct chpass3_arg {
+       krb5_ui_4 api_version;
+       krb5_principal princ;
+       krb5_boolean keepold;
+       int n_ks_tuple;
+       krb5_key_salt_tuple *ks_tuple;
+       char *pass;
+};
+typedef struct chpass3_arg chpass3_arg;
+bool_t xdr_chpass3_arg();
+
 struct setv4key_arg {
        krb5_ui_4 api_version;
        krb5_principal princ;
@@ -83,6 +106,18 @@ struct setkey_arg {
 typedef struct setkey_arg setkey_arg;
 bool_t xdr_setkey_arg();
 
+struct setkey3_arg {
+       krb5_ui_4 api_version;
+       krb5_principal princ;
+       krb5_boolean keepold;
+       int n_ks_tuple;
+       krb5_key_salt_tuple *ks_tuple;
+        krb5_keyblock *keyblocks;
+        int n_keys;
+};
+typedef struct setkey3_arg setkey3_arg;
+bool_t xdr_setkey3_arg();
+
 struct chrand_arg {
        krb5_ui_4 api_version;
        krb5_principal princ;
@@ -90,6 +125,16 @@ struct chrand_arg {
 typedef struct chrand_arg chrand_arg;
 bool_t xdr_chrand_arg();
 
+struct chrand3_arg {
+       krb5_ui_4 api_version;
+       krb5_principal princ;
+       krb5_boolean keepold;
+       int n_ks_tuple;
+       krb5_key_salt_tuple *ks_tuple;
+};
+typedef struct chrand3_arg chrand3_arg;
+bool_t xdr_chrand3_arg();
+
 struct chrand_ret {
        krb5_ui_4 api_version;
        kadm5_ret_t code;
@@ -223,3 +268,11 @@ extern gpols_ret *get_pols_1();
 extern generic_ret *setkey_principal_1();
 #define SETV4KEY_PRINCIPAL ((krb5_ui_4) 17)
 extern generic_ret *setv4key_principal_1();
+#define CREATE_PRINCIPAL3 ((krb5_ui_4) 18)
+extern generic_ret *create_principal3_1();
+#define CHPASS_PRINCIPAL3 ((krb5_ui_4) 19)
+extern generic_ret *chpass_principal3_1();
+#define CHRAND_PRINCIPAL3 ((krb5_ui_4) 20)
+extern chrand_ret *chrand_principal3_1();
+#define SETKEY_PRINCIPAL3 ((krb5_ui_4) 21)
+extern generic_ret *setkey_principal3_1();
index 63be6a0924eed1c920b9f9f7f6456d07ae7aab29..4e313a7496e419cb0e651c0f181bfd009bc707b6 100644 (file)
@@ -13,7 +13,6 @@
 static bool_t
 _xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp,
                             int v);
-
 /*
  * Function: xdr_ui_4
  *
@@ -243,6 +242,17 @@ bool_t xdr_krb5_key_data_nocontents(XDR *xdrs, krb5_key_data *objp)
      return (TRUE);
 }
 
+
+bool_t
+xdr_krb5_key_salt_tuple(XDR *xdrs, krb5_key_salt_tuple *objp)
+{
+    if (!xdr_krb5_enctype(xdrs, &objp->ks_enctype))
+       return FALSE;
+    if (!xdr_krb5_salttype(xdrs, &objp->ks_salttype))
+       return FALSE;
+    return TRUE;
+}
+
 bool_t xdr_krb5_tl_data(XDR *xdrs, krb5_tl_data **tl_data_head)
 {
      krb5_tl_data *tl, *tl2;
@@ -470,6 +480,39 @@ xdr_cprinc_arg(XDR *xdrs, cprinc_arg *objp)
        return (TRUE);
 }
 
+bool_t
+xdr_cprinc3_arg(XDR *xdrs, cprinc3_arg *objp)
+{
+       if (!xdr_ui_4(xdrs, &objp->api_version)) {
+               return (FALSE);
+       }
+       if (objp->api_version == KADM5_API_VERSION_1) {
+               if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) {
+                       return (FALSE);
+               }
+       } else {
+               if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) {
+                       return (FALSE);
+               }
+       }
+       if (!xdr_long(xdrs, &objp->mask)) {
+               return (FALSE);
+       }
+       if (!xdr_bool(xdrs, &objp->keepold)) {
+               return (FALSE);
+       }
+       if (!xdr_array(xdrs, (caddr_t *)&objp->ks_tuple,
+                      (unsigned int *)&objp->n_ks_tuple, ~0,
+                      sizeof(krb5_key_salt_tuple),
+                      xdr_krb5_key_salt_tuple)) {
+               return (FALSE);
+       }
+       if (!xdr_nullstring(xdrs, &objp->passwd)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
 bool_t
 xdr_generic_ret(XDR *xdrs, generic_ret *objp)
 {
@@ -579,6 +622,30 @@ xdr_chpass_arg(XDR *xdrs, chpass_arg *objp)
        return (TRUE);
 }
 
+bool_t
+xdr_chpass3_arg(XDR *xdrs, chpass3_arg *objp)
+{
+       if (!xdr_ui_4(xdrs, &objp->api_version)) {
+               return (FALSE);
+       }
+       if (!xdr_krb5_principal(xdrs, &objp->princ)) {
+               return (FALSE);
+       }
+       if (!xdr_bool(xdrs, &objp->keepold)) {
+               return (FALSE);
+       }
+       if (!xdr_array(xdrs, (caddr_t *)&objp->ks_tuple,
+                      objp->n_ks_tuple, ~0,
+                      sizeof(krb5_key_salt_tuple),
+                      xdr_krb5_key_salt_tuple)) {
+               return (FALSE);
+       }
+       if (!xdr_nullstring(xdrs, &objp->pass)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
 bool_t
 xdr_setv4key_arg(XDR *xdrs, setv4key_arg *objp)
 {
@@ -615,6 +682,31 @@ xdr_setkey_arg(XDR *xdrs, setkey_arg *objp)
        return (TRUE);
 }
 
+bool_t
+xdr_setkey3_arg(XDR *xdrs, setkey3_arg *objp)
+{
+       if (!xdr_ui_4(xdrs, &objp->api_version)) {
+               return (FALSE);
+       }
+       if (!xdr_krb5_principal(xdrs, &objp->princ)) {
+               return (FALSE);
+       }
+       if (!xdr_bool(xdrs, &objp->keepold)) {
+               return (FALSE);
+       }
+       if (!xdr_array(xdrs, (caddr_t *) &objp->ks_tuple,
+                      (unsigned int *) &objp->n_ks_tuple, ~0,
+                      sizeof(krb5_key_salt_tuple), xdr_krb5_key_salt_tuple)) {
+               return (FALSE);
+       }
+       if (!xdr_array(xdrs, (caddr_t *) &objp->keyblocks,
+                      (unsigned int *) &objp->n_keys, ~0,
+                      sizeof(krb5_keyblock), xdr_krb5_keyblock)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
 bool_t
 xdr_chrand_arg(XDR *xdrs, chrand_arg *objp)
 {
@@ -627,6 +719,27 @@ xdr_chrand_arg(XDR *xdrs, chrand_arg *objp)
        return (TRUE);
 }
 
+bool_t
+xdr_chrand3_arg(XDR *xdrs, chrand3_arg *objp)
+{
+       if (!xdr_ui_4(xdrs, &objp->api_version)) {
+               return (FALSE);
+       }
+       if (!xdr_krb5_principal(xdrs, &objp->princ)) {
+               return (FALSE);
+       }
+       if (!xdr_bool(xdrs, &objp->keepold)) {
+               return (FALSE);
+       }
+       if (!xdr_array(xdrs, (caddr_t *)&objp->ks_tuple,
+                      objp->n_ks_tuple, ~0,
+                      sizeof(krb5_key_salt_tuple),
+                      xdr_krb5_key_salt_tuple)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
 bool_t
 xdr_chrand_ret(XDR *xdrs, chrand_ret *objp)
 {
@@ -880,6 +993,14 @@ xdr_krb5_enctype(XDR *xdrs, krb5_enctype *objp)
    return (TRUE);
 }
 
+bool_t
+xdr_krb5_salttype(XDR *xdrs, krb5_int32 *objp)
+{
+    if (!xdr_int32(xdrs, (rpc_int32 *) objp))
+       return FALSE;
+    return TRUE;
+}
+
 bool_t
 xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp)
 {
@@ -893,4 +1014,3 @@ xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp)
       return FALSE;
    return TRUE;
 }
-
index 22e0cf1ecb507d85db92fcfe33da85606d2d740c..23d05d607e402ccc13b89e82d162dda785c567bd 100644 (file)
@@ -1,3 +1,7 @@
+2000-02-13  Tom Yu  <tlyu@mit.edu>
+
+       * svr_principal.c (kadm5_setkey_principal_3): New function.
+
 1999-10-26  Tom Yu  <tlyu@mit.edu>
 
        * Makefile.in: Clean up usage of CFLAGS, CPPFLAGS, DEFS, DEFINES,
index b58e11b5dc0bc298c33ae529698d66f6e72173b6..f8148a046fc062b599aea2c809c30dbf96eac1ab 100644 (file)
@@ -1432,15 +1432,31 @@ kadm5_setkey_principal(void *server_handle,
                       krb5_principal principal,
                       krb5_keyblock *keyblocks,
                       int n_keys)
+{
+    return
+       kadm5_setkey_principal_3(server_handle, principal,
+                                FALSE, 0, NULL,
+                                keyblocks, n_keys);
+}
+
+kadm5_ret_t
+kadm5_setkey_principal_3(void *server_handle,
+                        krb5_principal principal,
+                        krb5_boolean keepold,
+                        int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+                        krb5_keyblock *keyblocks,
+                        int n_keys)
 {
     krb5_db_entry              kdb;
     osa_princ_ent_rec          adb;
     krb5_int32                 now;
     kadm5_policy_ent_rec       pol;
-    krb5_key_data              *key_data;
+    krb5_key_data              *old_key_data;
+    int                                n_old_keys;
     int                                i, j, kvno, ret, last_pwd, have_pol = 0;
     kadm5_server_handle_t      handle = server_handle;
     krb5_boolean               similar;
+    krb5_keysalt               keysalt;
 
     CHECK_HANDLE(server_handle);
 
@@ -1459,10 +1475,17 @@ kadm5_setkey_principal(void *server_handle,
                                             &similar))
                return(ret);
            if (similar)
-               return KADM5_SETKEY_DUP_ENCTYPES;
+               if (n_ks_tuple) {
+                   if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
+                       return KADM5_SETKEY_DUP_ENCTYPES;
+               } else
+                   return KADM5_SETKEY_DUP_ENCTYPES;
        }
     }
 
+    if (n_ks_tuple != n_keys)
+       return KADM5_SETKEY3_ETYPE_MISMATCH;
+
     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return(ret);
     
@@ -1470,24 +1493,54 @@ kadm5_setkey_principal(void *server_handle,
         if (kdb.key_data[i].key_data_kvno > kvno)
              kvno = kdb.key_data[i].key_data_kvno;
 
-    if (kdb.key_data != NULL)
-        cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
+    if (keepold) {
+       old_key_data = kdb.key_data;
+       n_old_keys = kdb.n_key_data;
+    } else {
+       if (kdb.key_data != NULL)
+           cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
+       n_old_keys = 0;
+       old_key_data = NULL;
+    }
     
-    kdb.key_data = (krb5_key_data*)malloc(n_keys*sizeof(krb5_key_data));
+    kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys)
+                                         *sizeof(krb5_key_data));
     if (kdb.key_data == NULL)
         return ENOMEM;
-    memset(kdb.key_data, 0, n_keys*sizeof(krb5_key_data));
-    kdb.n_key_data = n_keys;
+    memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
+    kdb.n_key_data = 0;
 
     for (i = 0; i < n_keys; i++) {
-        if (ret = krb5_dbekd_encrypt_key_data(handle->context,
-                                              &master_keyblock,
-                                              &keyblocks[i], NULL,
-                                              kvno + 1,
-                                              &kdb.key_data[i]))
-             return ret;
+       if (n_ks_tuple) {
+           keysalt.type = ks_tuple[i].ks_salttype;
+           keysalt.data.length = 0;
+           keysalt.data.data = NULL;
+           if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
+               cleanup_key_data(handle->context, kdb.n_key_data,
+                                kdb.key_data);
+               return KADM5_SETKEY3_ETYPE_MISMATCH;
+           }
+       }
+       ret = krb5_dbekd_encrypt_key_data(handle->context,
+                                         &master_keyblock,
+                                         &keyblocks[i],
+                                         n_ks_tuple ? &keysalt : NULL,
+                                         kvno + 1,
+                                         &kdb.key_data[i]);
+       if (ret) {
+           cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
+           return ret;
+       }
+       kdb.n_key_data++;
     }
 
+    /* copy old key data if necessary */
+    for (i = 0; i < n_old_keys; i++) {
+       kdb.key_data[i+n_keys] = old_key_data[i];
+       memset(&old_key_data[i], 0, sizeof (krb5_key_data));
+       kdb.n_key_data++;
+    }
+    /* assert(kdb.n_key_data == n_keys + n_old_keys) */
     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
 
     if (ret = krb5_timeofday(handle->context, &now))