From: Tom Yu Date: Fri, 8 Oct 2010 03:57:28 +0000 (+0000) Subject: Add a kadm5 RPC for purging old keys from the KDB (e.g., from X-Git-Tag: krb5-1.9-beta1~25 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=1cc59c12550c828d487c622990d83481e8bbb6c5;p=krb5.git Add a kadm5 RPC for purging old keys from the KDB (e.g., from 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 --- diff --git a/doc/admin.texinfo b/doc/admin.texinfo index 9c0d2904e..bcabb62d7 100644 --- a/doc/admin.texinfo +++ b/doc/admin.texinfo @@ -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 diff --git a/src/kadmin/cli/kadmin.M b/src/kadmin/cli/kadmin.M index d6f2df522..7e6db2c61 100644 --- a/src/kadmin/cli/kadmin.M +++ b/src/kadmin/cli/kadmin.M @@ -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. diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c index ff6eeca6b..bf37bbedb 100644 --- a/src/kadmin/cli/kadmin.c +++ b/src/kadmin/cli/kadmin.c @@ -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; +} diff --git a/src/kadmin/cli/kadmin_ct.ct b/src/kadmin/cli/kadmin_ct.ct index 05a4efb84..6228f95ad 100644 --- a/src/kadmin/cli/kadmin_ct.ct +++ b/src/kadmin/cli/kadmin_ct.ct @@ -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, "?"; diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c index 76df26e79..a231d0b1f 100644 --- a/src/kadmin/server/kadm_rpc_svc.c +++ b/src/kadmin/server/kadm_rpc_svc.c @@ -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), diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c index 8e87616eb..4d7a26191 100644 --- a/src/kadmin/server/ovsec_kadmd.c +++ b/src/kadmin/server/ovsec_kadmd.c @@ -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; diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c index 29a8805ee..79bd2839f 100644 --- a/src/kadmin/server/server_stubs.c +++ b/src/kadmin/server/server_stubs.c @@ -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; diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 647226c7d..99837033b 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -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__ */ diff --git a/src/lib/kadm5/clnt/client_principal.c b/src/lib/kadm5/clnt/client_principal.c index 95d5c2dbd..019c50a28 100644 --- a/src/lib/kadm5/clnt/client_principal.c +++ b/src/lib/kadm5/clnt/client_principal.c @@ -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; +} diff --git a/src/lib/kadm5/clnt/client_rpc.c b/src/lib/kadm5/clnt/client_rpc.c index 184f154fc..95417a60c 100644 --- a/src/lib/kadm5/clnt/client_rpc.c +++ b/src/lib/kadm5/clnt/client_rpc.c @@ -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); +} diff --git a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports index 5e81580b1..249a3c774 100644 --- a/src/lib/kadm5/clnt/libkadm5clnt_mit.exports +++ b/src/lib/kadm5/clnt/libkadm5clnt_mit.exports @@ -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 diff --git a/src/lib/kadm5/kadm_rpc.h b/src/lib/kadm5/kadm_rpc.h index fb86605a4..1aa98d0fd 100644 --- a/src/lib/kadm5/kadm_rpc.h +++ b/src/lib/kadm5/kadm_rpc.h @@ -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__ */ diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c index 5fb67ebb1..0b14ff8f5 100644 --- a/src/lib/kadm5/kadm_rpc_xdr.c +++ b/src/lib/kadm5/kadm_rpc_xdr.c @@ -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) { diff --git a/src/lib/kadm5/srv/libkadm5srv_mit.exports b/src/lib/kadm5/srv/libkadm5srv_mit.exports index 345957a13..49a1b8803 100644 --- a/src/lib/kadm5/srv/libkadm5srv_mit.exports +++ b/src/lib/kadm5/srv/libkadm5srv_mit.exports @@ -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 diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c index e50c92237..696362ac6 100644 --- a/src/lib/kadm5/srv/svr_principal.c +++ b/src/lib/kadm5/srv/svr_principal.c @@ -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; +}