Add a kadm5 RPC for purging old keys from the KDB (e.g., from
authorTom Yu <tlyu@mit.edu>
Fri, 8 Oct 2010 03:57:28 +0000 (03:57 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 8 Oct 2010 03:57:28 +0000 (03:57 +0000)
change_password -keepold), and add a kadmin CLI command for it.

Keeping ticket open because an automated test needs to be added.

Long-term future work includes start/expire dates on keys, or
not-yet-valid flags.

ticket: 1219
status: open
target_version: 1.9

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

15 files changed:
doc/admin.texinfo
src/kadmin/cli/kadmin.M
src/kadmin/cli/kadmin.c
src/kadmin/cli/kadmin_ct.ct
src/kadmin/server/kadm_rpc_svc.c
src/kadmin/server/ovsec_kadmd.c
src/kadmin/server/server_stubs.c
src/lib/kadm5/admin.h
src/lib/kadm5/clnt/client_principal.c
src/lib/kadm5/clnt/client_rpc.c
src/lib/kadm5/clnt/libkadm5clnt_mit.exports
src/lib/kadm5/kadm_rpc.h
src/lib/kadm5/kadm_rpc_xdr.c
src/lib/kadm5/srv/libkadm5srv_mit.exports
src/lib/kadm5/srv/svr_principal.c

index 9c0d2904e04b04d4efe550814d5125f0a5bd096f..bcabb62d771ce0d23e46b5f9212df6454622c3a7 100644 (file)
@@ -2586,10 +2586,10 @@ earlier than krb5-1.2.  See @ref{Supported Encryption Types} and
 @ref{Salts} for possible values.
 
 @item @b{-keepold}
-Keeps the previous kvno's keys around.  There is no easy way to delete
-the old keys, and this flag is usually not necessary except perhaps for
-TGS keys.  Don't use this flag unless you know what you're doing. This 
-option is not supported for the LDAP database
+Keeps the previous kvno's keys around.  This flag is usually not
+necessary except perhaps for TGS keys.  Don't use this flag unless you
+know what you're doing. This option is not supported for the LDAP
+database
 
 
 
@@ -4015,14 +4015,11 @@ well as the new key.  For example:
 @end group
 @end smallexample
 
-There is currently no way to remove the old key without running
-@code{change_password} without the @b{-keepold} flag (and thereby
-invalidating all existing TGTs).  After issuing this command, the old
-key is still valid and is still vulnerable to (for instance) brute force
-attacks.  To completely retire an old key or encryption type, it's
-therefore currently necessary to declare a flag day, run
-@code{change_password} without the @b{-keepold} flag, and force all
-users to acquire new tickets.
+After issuing this command, the old key is still valid and is still
+vulnerable to (for instance) brute force attacks.  To completely
+retire an old key or encryption type, run the @code{purgekeys} command
+to delete keys with older kvnos, ideally first making sure that all
+tickets issued with the old keys have expired.
 
 @node Configuring Kerberos with OpenLDAP back-end, Application Servers, Administrating the Kerberos Database, Top
 @chapter Configuring Kerberos with OpenLDAP back-end
index d6f2df522425714ab7b2674d10f88a5e3a155371..7e6db2c61b53a72a77fa29633466ab61c77cab5a 100644 (file)
@@ -562,8 +562,7 @@ enctype\-salttype pairs.  This will not function against kadmin
 daemons earlier than krb5\-1.2.
 .TP
 \fB\-keepold \fP 
-Keeps the previous kvno's keys around.  There is no
-easy way to delete the old keys, and this flag is usually not
+Keeps the previous kvno's keys around.  This flag is usually not
 necessary except perhaps for TGS keys.  Don't use this flag unless you
 know what you're doing. This option is not supported for the LDAP database.
 .nf
@@ -586,6 +585,18 @@ expired)
 .RE
 .fi
 .TP
+\fBpurgekeys\fP [\fB-keepkvno\fP \fIoldest_kvno_to_keep\fP] \fIprincipal\fP
+purges previously retained old keys (e.g., from
+.B change_password
+.BR -keepold )
+from
+.IR principal .
+If
+.B -keepkvno
+is specified, then only purges keys with kvnos lower than
+.IR oldest_kvno_to_keep .
+.fi
+.TP
 \fBget_principal\fP [\fB-terse\fP] \fIprincipal\fP
 gets the attributes of
 .IR principal .
@@ -922,9 +933,3 @@ OpenVision Kerberos administration program.
 .SH BUGS
 .PP
 Command output needs to be cleaned up.
-
-There is no way to delete a key kept around from a "\-keepold" option
-to a password-changing command, other than to do a password change
-without the "\-keepold" option, which will of course cause problems if
-the key is a TGS key.  There will be more powerful key-manipulation
-commands in the future.
index ff6eeca6b7e63f148f5e7607f6c981141d23d54a..bf37bbedb2b343233a2ce50193b7c62fbb4797f7 100644 (file)
@@ -1742,3 +1742,50 @@ kadmin_getprivs(int argc, char *argv[])
     }
     printf("\n");
 }
+
+void
+kadmin_purgekeys(int argc, char *argv[])
+{
+    kadm5_ret_t retval;
+    int keepkvno = -1;
+    char *pname = NULL, *canon = NULL;
+    krb5_principal princ;
+
+    if (argc == 4 && strcmp(argv[1], "-keepkvno") == 0) {
+        keepkvno = atoi(argv[2]);
+        pname = argv[3];
+    }
+    if (argc == 2) {
+        pname = argv[1];
+    }
+    if (pname == NULL) {
+        fprintf(stderr, "usage: purgekeys [-keepkvno oldest_kvno_to_keep] "
+                "principal\n");
+        return;
+    }
+
+    retval = kadmin_parse_name(pname, &princ);
+    if (retval) {
+        com_err("purgekeys", retval, "while parsing principal");
+        return;
+    }
+
+    retval = krb5_unparse_name(context, princ, &canon);
+    if (retval) {
+        com_err("purgekeys", retval, "while canonicalizing principal");
+        goto cleanup;
+    }
+
+    retval = kadm5_purgekeys(handle, princ, keepkvno);
+    if (retval) {
+        com_err("purgekeys", retval,
+                "while purging keys for principal \"%s\"", canon);
+        goto cleanup;
+    }
+
+    printf("Old keys for principal \"%s\" purged.\n", canon);
+cleanup:
+    krb5_free_principal(context, princ);
+    free(canon);
+    return;
+}
index 05a4efb840c7fb6a772b4e7868d79b9f62f1c01b..6228f95ad91dd00864848d2218214d24c038e831 100644 (file)
@@ -74,6 +74,9 @@ request kadmin_lock, "Lock database exclusively (use with extreme caution!)",
 request kadmin_unlock, "Release exclusive database lock",
        unlock;
 
+request kadmin_purgekeys, "Purge previously retained old keys from a principal",
+       purgekeys;
+
 # list_requests is generic -- unrelated to Kerberos
 request        ss_list_requests, "List available requests.",
        list_requests, lr, "?";
index 76df26e79b9457b92675d5fbd79fbc858518fa80..a231d0b1f296c95d9ecb460df642dc49492025d7 100644 (file)
@@ -212,6 +212,12 @@ void kadm_1(rqstp, transp)
          local = (char *(*)()) setkey_principal3_2_svc;
          break;
 
+     case PURGEKEYS:
+         xdr_argument = xdr_purgekeys_arg;
+         xdr_result = xdr_generic_ret;
+         local = (char *(*)()) purgekeys_2_svc;
+         break;
+
      default:
          krb5_klog_syslog(LOG_ERR, "Invalid KADM5 procedure number: %s, %d",
                 inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
index 8e87616ebdfc5f06e7779e47a2f4da8ae13b1aaf..4d7a26191211dd93b2ca9140566ecedd6ff537d3 100644 (file)
@@ -922,7 +922,8 @@ void log_badverf(gss_name_t client_name, gss_name_t server_name,
         {18, "CREATE_PRINCIPAL3"},
         {19, "CHPASS_PRINCIPAL3"},
         {20, "CHRAND_PRINCIPAL3"},
-        {21, "SETKEY_PRINCIPAL3"}
+        {21, "SETKEY_PRINCIPAL3"},
+        {22, "PURGEKEYS"}
     };
 #define NPROCNAMES (sizeof (proc_names) / sizeof (struct procnames))
     OM_uint32 minor;
index 29a8805eeb4728a558da786287f378fd6a737492..79bd2839fca54ce0875c58ed690e25e3cd697a20 100644 (file)
@@ -1564,6 +1564,63 @@ exit_func:
     return &ret;
 }
 
+generic_ret *
+purgekeys_2_svc(purgekeys_arg *arg, struct svc_req *rqstp)
+{
+    static generic_ret          ret;
+    char                        *prime_arg, *funcname;
+    gss_buffer_desc             client_name, service_name;
+    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;
+
+    funcname = "kadm5_purgekeys";
+
+    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_MODIFY,
+                               arg->princ, NULL)) {
+        ret.code = KADM5_AUTH_MODIFY;
+        log_unauth(funcname, prime_arg, &client_name, &service_name, rqstp);
+    } else {
+        ret.code = kadm5_purgekeys((void *)handle, arg->princ,
+                                   arg->keepkvno);
+        if (ret.code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret.code);
+
+        log_done(funcname, prime_arg, errmsg ? errmsg : "success",
+                 &client_name, &service_name, rqstp);
+
+        if (errmsg != NULL)
+            krb5_free_error_message(handle->context, errmsg);
+    }
+    free(prime_arg);
+    gss_release_buffer(&minor_stat, &client_name);
+    gss_release_buffer(&minor_stat, &service_name);
+exit_func:
+    free_server_handle(handle);
+    return &ret;
+}
+
 generic_ret *init_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp)
 {
     static generic_ret         ret;
index 647226c7d96e1287a6de412488b2e863adb26278..99837033bee397dc403c9de80b1d62be88b072c1 100644 (file)
@@ -508,6 +508,11 @@ kadm5_ret_t    kadm5_get_principal_keys(void *server_handle,
                                         krb5_keyblock **keyblocks,
                                         int *n_keys);
 
+
+kadm5_ret_t    kadm5_purgekeys(void *server_handle,
+                               krb5_principal principal,
+                               int keepkvno);
+
 KADM5INT_END_DECLS
 
 #endif /* __KADM5_ADMIN_H__ */
index 95d5c2dbd38b75505ea0d5f8d680468fbff98850..019c50a28f9c3fe53f4baef16216075c5519a917 100644 (file)
@@ -463,3 +463,26 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
 {
     return EINVAL;
 }
+
+kadm5_ret_t
+kadm5_purgekeys(void *server_handle,
+                krb5_principal princ,
+                int keepkvno)
+{
+    purgekeys_arg       arg;
+    generic_ret         *r;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    arg.princ = princ;
+    arg.keepkvno = keepkvno;
+    arg.api_version = handle->api_version;
+
+    if (princ == NULL)
+        return EINVAL;
+    r = purgekeys_2(&arg, handle->clnt);
+    if(r == NULL)
+        eret();
+    return r->code;
+}
index 184f154fca5df24530ca430408c807210cdee1fc..95417a60ce80682b41d5a2be315c59f5dda4e7ea 100644 (file)
@@ -326,3 +326,18 @@ init_2(void *argp, CLIENT *clnt)
      }
      return (&clnt_res);
 }
+
+generic_ret *
+purgekeys_2(purgekeys_arg *argp, CLIENT *clnt)
+{
+     static generic_ret clnt_res;
+
+     memset(&clnt_res, 0, sizeof(clnt_res));
+     if (clnt_call(clnt, PURGEKEYS,
+                  (xdrproc_t) xdr_purgekeys_arg, (caddr_t) argp,
+                  (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res,
+                  TIMEOUT) != RPC_SUCCESS) {
+         return (NULL);
+     }
+     return (&clnt_res);
+}
index 5e81580b19dd71defc85ab208fe35deb9e60b10b..249a3c774c1ee48d05db15a2524f2ff30092376f 100644 (file)
@@ -32,6 +32,7 @@ kadm5_init_with_skey
 kadm5_lock
 kadm5_modify_policy
 kadm5_modify_principal
+kadm5_purgekeys
 kadm5_randkey_principal
 kadm5_randkey_principal_3
 kadm5_rename_principal
index fb86605a49b41f4508963dee55e6b2c7dde6df76..1aa98d0fda7497826340166d7a8c85812ebda9ca 100644 (file)
@@ -200,6 +200,13 @@ struct getprivs_ret {
 };
 typedef struct getprivs_ret getprivs_ret;
 
+struct purgekeys_arg {
+       krb5_ui_4 api_version;
+       krb5_principal princ;
+       int keepkvno;
+};
+typedef struct purgekeys_arg purgekeys_arg;
+
 #define KADM 2112
 #define KADMVERS 2
 #define CREATE_PRINCIPAL 1
@@ -265,6 +272,9 @@ extern  chrand_ret * chrand_principal3_2_svc(chrand3_arg *, struct svc_req *);
 #define SETKEY_PRINCIPAL3 21
 extern  generic_ret * setkey_principal3_2(setkey3_arg *, CLIENT *);
 extern  generic_ret * setkey_principal3_2_svc(setkey3_arg *, struct svc_req *);
+#define PURGEKEYS 22
+extern  generic_ret * purgekeys_2(purgekeys_arg *, CLIENT *);
+extern  generic_ret * purgekeys_2_svc(purgekeys_arg *, struct svc_req *);
 
 extern bool_t xdr_cprinc_arg ();
 extern bool_t xdr_cprinc3_arg ();
@@ -301,6 +311,7 @@ extern bool_t xdr_gpol_ret ();
 extern bool_t xdr_gpols_arg ();
 extern bool_t xdr_gpols_ret ();
 extern bool_t xdr_getprivs_ret ();
+extern bool_t xdr_purgekeys_arg ();
 
 
 #endif /* __KADM_RPC_H__ */
index 5fb67ebb1c3d7c4ffdd55b2e75dff6eb47945855..0b14ff8f52135eeba2b9524fd75e4e0303c632b0 100644 (file)
@@ -955,6 +955,22 @@ bool_t xdr_getprivs_ret(XDR *xdrs, getprivs_ret *objp)
      return TRUE;
 }
 
+bool_t
+xdr_purgekeys_arg(XDR *xdrs, purgekeys_arg *objp)
+{
+       if (!xdr_ui_4(xdrs, &objp->api_version)) {
+               return (FALSE);
+       }
+       if (!xdr_krb5_principal(xdrs, &objp->princ)) {
+               return (FALSE);
+       }
+       if (!xdr_int(xdrs, &objp->keepkvno)) {
+            return FALSE;
+       }
+
+       return (TRUE);
+}
+
 bool_t
 xdr_krb5_principal(XDR *xdrs, krb5_principal *objp)
 {
index 345957a139d02bb33ce5fef6a591a19ef50c7a73..49a1b880310451feeb9e0fb48e9b7139b2320d89 100644 (file)
@@ -43,6 +43,7 @@ kadm5_lock
 kadm5_modify_policy
 kadm5_modify_policy_internal
 kadm5_modify_principal
+kadm5_purgekeys
 kadm5_randkey_principal
 kadm5_randkey_principal_3
 kadm5_rename_principal
@@ -129,6 +130,7 @@ xdr_nullstring
 xdr_nulltype
 xdr_osa_princ_ent_rec
 xdr_osa_pw_hist_ent
+xdr_purgekeys_arg
 xdr_rprinc_arg
 xdr_setkey3_arg
 xdr_setkey_arg
index e50c92237954f9c250083ff29892657e9373a160..696362ac60ed42be499611e30320b511960c3a59 100644 (file)
@@ -2219,3 +2219,65 @@ kadm5_ret_t kadm5_decrypt_key(void *server_handle,
 
     return KADM5_OK;
 }
+
+kadm5_ret_t
+kadm5_purgekeys(void *server_handle,
+                krb5_principal principal,
+                int keepkvno)
+{
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_ret_t ret;
+    krb5_db_entry *kdb;
+    osa_princ_ent_rec adb;
+    krb5_key_data *old_keydata;
+    int n_old_keydata;
+    int i, j, k;
+
+    CHECK_HANDLE(server_handle);
+
+    if (principal == NULL)
+        return EINVAL;
+
+    ret = kdb_get_entry(handle, principal, &kdb, &adb);
+    if (ret)
+        return(ret);
+
+    if (keepkvno <= 0) {
+        keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
+                                             kdb->key_data);
+    }
+
+    old_keydata = kdb->key_data;
+    n_old_keydata = kdb->n_key_data;
+    kdb->n_key_data = 0;
+    kdb->key_data = krb5_db_alloc(handle->context, NULL,
+                                  n_old_keydata * sizeof(krb5_key_data));
+    if (kdb->key_data == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
+    for (i = 0, j = 0; i < n_old_keydata; i++) {
+        if (old_keydata[i].key_data_kvno < keepkvno)
+            continue;
+
+        /* Alias the key_data_contents pointers; we null them out in the
+         * source array immediately after. */
+        kdb->key_data[j] = old_keydata[i];
+        for (k = 0; k < old_keydata[i].key_data_ver; k++) {
+            old_keydata[i].key_data_contents[k] = NULL;
+        }
+        j++;
+    }
+    kdb->n_key_data = j;
+    cleanup_key_data(handle->context, n_old_keydata, old_keydata);
+
+    kdb->mask = KADM5_KEY_DATA;
+    ret = kdb_put_entry(handle, kdb, &adb);
+    if (ret)
+        goto done;
+
+done:
+    kdb_free_entry(handle, kdb, &adb);
+    return ret;
+}