diff --git a/SOURCES/0001-Don-t-leak-the-creds-we-don-t-plan-to-copy-over.patch b/SOURCES/0001-Don-t-leak-the-creds-we-don-t-plan-to-copy-over.patch new file mode 100644 index 0000000..9e5757a --- /dev/null +++ b/SOURCES/0001-Don-t-leak-the-creds-we-don-t-plan-to-copy-over.patch @@ -0,0 +1,29 @@ +From 5733669cb88800f0e24c53b12a09034bbf342442 Mon Sep 17 00:00:00 2001 +From: Nalin Dahyabhai +Date: Mon, 17 Feb 2014 17:14:20 -0500 +Subject: [PATCH 1/4] Don't leak the creds we don't plan to copy over + +--- + src/lib/krb5/krb/vfy_increds.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/lib/krb5/krb/vfy_increds.c b/src/lib/krb5/krb/vfy_increds.c +index e88a37f..4833992 100644 +--- a/src/lib/krb5/krb/vfy_increds.c ++++ b/src/lib/krb5/krb/vfy_increds.c +@@ -69,9 +69,9 @@ copy_creds_except(krb5_context context, krb5_ccache incc, + + while (!(ret = krb5_cc_next_cred(context, incc, &cur, &creds))) { + if (krb5_principal_compare(context, princ, creds.server)) +- continue; +- +- ret = krb5_cc_store_cred(context, outcc, &creds); ++ ret = 0; ++ else ++ ret = krb5_cc_store_cred(context, outcc, &creds); + krb5_free_cred_contents(context, &creds); + if (ret) + goto cleanup; +-- +1.9.0 + diff --git a/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch b/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch index 2a6275c..b3153c8 100644 --- a/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch +++ b/SOURCES/0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch @@ -1,4 +1,4 @@ -From 7e0bcb958eb5861cb30a190dcac1e6422d65299e Mon Sep 17 00:00:00 2001 +From a787aa8259e44a3aec5b03ff9773c9ea4f4bc7f5 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Fri, 1 Nov 2013 09:48:13 -0400 Subject: [PATCH 1/6] Don't try to stat() not-on-disk ccache residuals @@ -7,10 +7,11 @@ Don't assume that ccache residual names are filenames which we can stat() usefully. Instead, use helper functions to call the library routines to try to read the default principal name from caches. --- - src/clients/ksu/ccache.c | 88 +++++++++++++++++++++++++++++------------------- - src/clients/ksu/ksu.h | 6 ++++ - src/clients/ksu/main.c | 17 +++++----- - 3 files changed, 68 insertions(+), 43 deletions(-) + src/clients/ksu/ccache.c | 88 +++++++++++++++++++++++++++------------------ + src/clients/ksu/heuristic.c | 13 ++----- + src/clients/ksu/ksu.h | 6 ++++ + src/clients/ksu/main.c | 17 +++++---- + 4 files changed, 70 insertions(+), 54 deletions(-) diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c index 9916c75..7917af2 100644 @@ -190,6 +191,52 @@ index 9916c75..7917af2 100644 + krb5_free_string(context, def_cc_name); + return result; +} +diff --git a/src/clients/ksu/heuristic.c b/src/clients/ksu/heuristic.c +index c7e691c..bfde451 100644 +--- a/src/clients/ksu/heuristic.c ++++ b/src/clients/ksu/heuristic.c +@@ -404,12 +404,8 @@ krb5_error_code find_either_ticket (context, cc, client, end_server, found) + krb5_principal kdc_server; + krb5_error_code retval; + krb5_boolean temp_found = FALSE; +- const char * cc_source_name; +- struct stat st_temp; + +- cc_source_name = krb5_cc_get_name(context, cc); +- +- if ( ! stat(cc_source_name, &st_temp)){ ++ if (krb5_ccache_is_initialized(context, cc)) { + + retval = find_ticket(context, cc, client, end_server, &temp_found); + if (retval) +@@ -546,7 +542,6 @@ krb5_error_code get_best_princ_for_target(context, source_uid, target_uid, + { + + princ_info princ_trials[10]; +- const char * cc_source_name; + krb5_principal cc_def_princ = NULL; + krb5_principal temp_client; + krb5_principal target_client; +@@ -558,7 +553,6 @@ krb5_error_code get_best_princ_for_target(context, source_uid, target_uid, + struct stat tb; + int count =0; + int i; +- struct stat st_temp; + + *path_out = 0; + +@@ -566,10 +560,7 @@ krb5_error_code get_best_princ_for_target(context, source_uid, target_uid, + if (options->princ) + return 0; + +- cc_source_name = krb5_cc_get_name(context, cc_source); +- +- +- if (! stat(cc_source_name, &st_temp)) { ++ if (krb5_ccache_is_initialized(context, cc_source)) { + retval = krb5_cc_get_principal(context, cc_source, &cc_def_princ); + if (retval) + return retval; diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h index f2c0811..2a63c21 100644 --- a/src/clients/ksu/ksu.h diff --git a/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch b/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch index 7b900de..6fed2d8 100644 --- a/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch +++ b/SOURCES/0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch @@ -1,4 +1,4 @@ -From 60295a63fadf04f6cd6db7919aa1dad6fb4f0596 Mon Sep 17 00:00:00 2001 +From ef82ee237510535238fb2b1e2fbf60112aaf70d1 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 30 Oct 2013 21:45:35 -0400 Subject: [PATCH 2/6] Use an in-memory cache until we need the target's diff --git a/SOURCES/0003-Handlers-won-t-take-care-of-initializing-err.patch b/SOURCES/0003-Handlers-won-t-take-care-of-initializing-err.patch new file mode 100644 index 0000000..db73965 --- /dev/null +++ b/SOURCES/0003-Handlers-won-t-take-care-of-initializing-err.patch @@ -0,0 +1,27 @@ +From f93d963198c2b98e02fa279b39b01772e88b2259 Mon Sep 17 00:00:00 2001 +From: Nalin Dahyabhai +Date: Tue, 11 Mar 2014 13:26:32 -0400 +Subject: [PATCH 3/4] Handlers won't take care of initializing 'err' + +We can get an KRB5_KDC_UNREACH error back without processing a server +reply first, so we should go ahead and initialize err for that case. +--- + src/lib/krb5/os/sendto_kdc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c +index 828bfff..23b2162 100644 +--- a/src/lib/krb5/os/sendto_kdc.c ++++ b/src/lib/krb5/os/sendto_kdc.c +@@ -166,7 +166,7 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message, + const krb5_data *realm, krb5_data *reply, int *use_master, + int tcp_only) + { +- krb5_error_code retval, err; ++ krb5_error_code retval, err = 0; + struct serverlist servers; + int socktype1 = 0, socktype2 = 0, server_used; + +-- +1.9.0 + diff --git a/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch b/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch index 9a55929..c78bcba 100644 --- a/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch +++ b/SOURCES/0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch @@ -1,4 +1,4 @@ -From b92db8dabc566ba0fee4d122ce4f7fb11fe8a637 Mon Sep 17 00:00:00 2001 +From bb1793f369fc486362d3d540eaa19799164d717f Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 31 Oct 2013 15:43:49 -0400 Subject: [PATCH 3/6] Learn to destroy the ccache we're copying from diff --git a/SOURCES/0004-Don-t-leak-the-per-request-preauth-context.patch b/SOURCES/0004-Don-t-leak-the-per-request-preauth-context.patch new file mode 100644 index 0000000..f6d3258 --- /dev/null +++ b/SOURCES/0004-Don-t-leak-the-per-request-preauth-context.patch @@ -0,0 +1,24 @@ +From 37fd72ac18648155bb4715ac6afd40f471ba01fa Mon Sep 17 00:00:00 2001 +From: Nalin Dahyabhai +Date: Tue, 11 Mar 2014 13:33:23 -0400 +Subject: [PATCH 4/4] Don't leak the per-request preauth context + +--- + src/lib/krb5/krb/get_in_tkt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c +index ebd2844..ebcb362 100644 +--- a/src/lib/krb5/krb/get_in_tkt.c ++++ b/src/lib/krb5/krb/get_in_tkt.c +@@ -488,6 +488,7 @@ krb5_init_creds_free(krb5_context context, + k5_response_items_free(ctx->rctx.items); + free(ctx->in_tkt_service); + zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length); ++ krb5_preauth_request_context_fini(context); + krb5_free_error(context, ctx->err_reply); + krb5_free_pa_data(context, ctx->err_padata); + krb5_free_cred_contents(context, &ctx->cred); +-- +1.9.0 + diff --git a/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch b/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch index 01d8430..b822a09 100644 --- a/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch +++ b/SOURCES/0004-Try-to-use-the-default_ccache_name-d-as-the-target.patch @@ -1,4 +1,4 @@ -From 07f4a0b9c7d68e39a41c085e2f6323093b89ac36 Mon Sep 17 00:00:00 2001 +From d0a3250bd384b5dd524f102f97c9c1edc1fe00fb Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 30 Oct 2013 21:47:14 -0400 Subject: [PATCH 4/6] Try to use the default_ccache_name'd as the target diff --git a/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch b/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch index d939cd4..367efef 100644 --- a/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch +++ b/SOURCES/0005-Be-more-careful-of-target-ccache-collections.patch @@ -1,4 +1,4 @@ -From cd193e20361624c8a0dc43634c31c6ac2d3d150d Mon Sep 17 00:00:00 2001 +From b39abd936b6422716d0d6edf5b37326bbe095da7 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 30 Oct 2013 21:34:27 -0400 Subject: [PATCH 5/6] Be more careful of target ccache collections diff --git a/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch b/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch index 0d931eb..10608ca 100644 --- a/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch +++ b/SOURCES/0006-Copy-config-entries-to-the-target-ccache.patch @@ -1,4 +1,4 @@ -From 3eeefbd93d0fa7dee9fba9a77fffbc959b83f056 Mon Sep 17 00:00:00 2001 +From 0e6e95235499818800d6ca15124a5860f0d92a2b Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 29 Oct 2013 16:27:20 -0400 Subject: [PATCH 6/6] Copy config entries to the target ccache diff --git a/SOURCES/krb5-1.11-preauthcore.patch b/SOURCES/krb5-1.11-preauthcore.patch new file mode 100644 index 0000000..27c6692 --- /dev/null +++ b/SOURCES/krb5-1.11-preauthcore.patch @@ -0,0 +1,12 @@ +Backport a small fix from 1.12 (#1035203). + +--- src/lib/krb5/krb/preauth2.c ++++ src/lib/krb5/krb/preauth2.c +@@ -1002,6 +1002,7 @@ krb5_do_preauth(krb5_context context, kr + krb5_init_preauth_context(context); + if (context->preauth_context == NULL) { + *out_padata = out_pa_list; ++ out_pa_list = NULL; + goto error; + } + diff --git a/SOURCES/krb5-1.11-rcache-acquirecred-source.patch b/SOURCES/krb5-1.11-rcache-acquirecred-source.patch new file mode 100644 index 0000000..d67d5c7 --- /dev/null +++ b/SOURCES/krb5-1.11-rcache-acquirecred-source.patch @@ -0,0 +1,138 @@ +Adapted. + +commit 7dad0bee30fbbde8cfc0eacd2d1487c198a004a1 +Author: Simo Sorce +Date: Thu Dec 26 19:05:34 2013 -0500 + + Add rcache feature to gss_acquire_cred_from + + The "rcache" cred store entry can specify a replay cache type and name + to be used with the credentials being acquired. + + [ghudson@mit.edu: split up, simplified, and altered to fit preparatory + commits] + + ticket: 7819 (new) + +diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c +index f625c0c..5d680f9 100644 +--- a/src/lib/gssapi/krb5/acquire_cred.c ++++ b/src/lib/gssapi/krb5/acquire_cred.c +@@ -180,7 +180,8 @@ cleanup: + + static OM_uint32 + acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, +- krb5_keytab req_keytab, krb5_gss_cred_id_rec *cred) ++ krb5_keytab req_keytab, const char *rcname, ++ krb5_gss_cred_id_rec *cred) + { + OM_uint32 major; + krb5_error_code code; +@@ -189,6 +190,20 @@ acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, + + assert(cred->keytab == NULL); + ++ /* If we have an explicit rcache name, open it. */ ++ if (rcname != NULL) { ++ code = krb5_rc_resolve_full(context, &rc, rcname); ++ if (code) { ++ major = GSS_S_FAILURE; ++ goto cleanup; ++ } ++ code = krb5_rc_recover_or_initialize(context, rc, context->clockskew); ++ if (code) { ++ major = GSS_S_FAILURE; ++ goto cleanup; ++ } ++ } ++ + if (req_keytab != NULL) { + char ktname[BUFSIZ]; + +@@ -221,12 +236,14 @@ acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, + goto cleanup; + } + +- /* Open the replay cache for this principal. */ +- code = krb5_get_server_rcache(context, &cred->name->princ->data[0], +- &rc); +- if (code) { +- major = GSS_S_FAILURE; +- goto cleanup; ++ if (rc == NULL) { ++ /* Open the replay cache for this principal. */ ++ code = krb5_get_server_rcache(context, &cred->name->princ->data[0], ++ &rc); ++ if (code) { ++ major = GSS_S_FAILURE; ++ goto cleanup; ++ } + } + } else { + /* Make sure we have a keytab with keys in it. */ +@@ -718,8 +735,8 @@ acquire_cred_context(krb5_context context, OM_uint32 *minor_status, + gss_name_t desired_name, gss_buffer_t password, + OM_uint32 time_req, gss_cred_usage_t cred_usage, + krb5_ccache ccache, krb5_keytab client_keytab, +- krb5_keytab keytab, krb5_boolean iakerb, +- gss_cred_id_t *output_cred_handle, ++ krb5_keytab keytab, const char *rcname, ++ krb5_boolean iakerb, gss_cred_id_t *output_cred_handle, + OM_uint32 *time_rec) + { + krb5_gss_cred_id_t cred = NULL; +@@ -775,7 +792,7 @@ acquire_cred_context(krb5_context context, OM_uint32 *minor_status, + * in cred->name if desired_princ is specified. + */ + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { +- ret = acquire_accept_cred(context, minor_status, keytab, cred); ++ ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred); + if (ret != GSS_S_COMPLETE) + goto error_out; + } +@@ -867,7 +884,7 @@ acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + + ret = acquire_cred_context(context, minor_status, desired_name, password, + time_req, cred_usage, ccache, NULL, keytab, +- iakerb, output_cred_handle, time_rec); ++ NULL, iakerb, output_cred_handle, time_rec); + + out: + krb5_free_context(context); +@@ -1135,7 +1152,7 @@ krb5_gss_acquire_cred_from(OM_uint32 *minor_status, + krb5_keytab client_keytab = NULL; + krb5_keytab keytab = NULL; + krb5_ccache ccache = NULL; +- const char *value; ++ const char *rcname, *value; + OM_uint32 ret; + + code = gss_krb5int_initialize_library(); +@@ -1191,9 +1208,14 @@ krb5_gss_acquire_cred_from(OM_uint32 *minor_status, + } + } + ++ ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname); ++ if (GSS_ERROR(ret)) ++ goto out; ++ + ret = acquire_cred_context(context, minor_status, desired_name, NULL, + time_req, cred_usage, ccache, client_keytab, +- keytab, 0, output_cred_handle, time_rec); ++ keytab, rcname, 0, output_cred_handle, ++ time_rec); + + out: + if (ccache != NULL) +diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h +index 0167816..8e4f6d9 100644 +--- a/src/lib/gssapi/krb5/gssapiP_krb5.h ++++ b/src/lib/gssapi/krb5/gssapiP_krb5.h +@@ -1260,6 +1260,7 @@ data_to_gss(krb5_data *input_k5data, gss_buffer_t output_buffer) + #define KRB5_CS_CLI_KEYTAB_URN "client_keytab" + #define KRB5_CS_KEYTAB_URN "keytab" + #define KRB5_CS_CCACHE_URN "ccache" ++#define KRB5_CS_RCACHE_URN "rcache" + + OM_uint32 + kg_value_from_cred_store(gss_const_key_value_set_t cred_store, diff --git a/SOURCES/krb5-1.11-rcache-acquirecred-test.patch b/SOURCES/krb5-1.11-rcache-acquirecred-test.patch new file mode 100644 index 0000000..ec1709b --- /dev/null +++ b/SOURCES/krb5-1.11-rcache-acquirecred-test.patch @@ -0,0 +1,90 @@ +Adapted, with some more intermediate context applied in between. + +commit 6f8d5135334c9ddb674f9824e750872b3b0642ea +Author: Greg Hudson +Date: Thu Jan 16 11:49:55 2014 -0500 + + Add test for gss_acquire_cred_from rcache feature + +diff --git a/src/tests/gssapi/t_credstore.c b/src/tests/gssapi/t_credstore.c +index 575f96d..e28f5d0 100644 +--- a/src/tests/gssapi/t_credstore.c ++++ b/src/tests/gssapi/t_credstore.c +@@ -46,7 +46,9 @@ main(int argc, char *argv[]) + gss_cred_usage_t cred_usage = GSS_C_BOTH; + gss_OID_set mechs = GSS_C_NO_OID_SET; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; +- krb5_boolean store_creds = FALSE; ++ gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT; ++ gss_buffer_desc itok, atok; ++ krb5_boolean store_creds = FALSE, replay = FALSE; + char opt; + + /* Parse options. */ +@@ -54,6 +56,8 @@ main(int argc, char *argv[]) + opt = (*argv)[1]; + if (opt == 's') + store_creds = TRUE; ++ else if (opt == 'r') ++ replay = TRUE; + else if (opt == 'a') + cred_usage = GSS_C_ACCEPT; + else if (opt == 'b') +@@ -101,6 +105,31 @@ main(int argc, char *argv[]) + &store, &cred, NULL, NULL); + check_gsserr("gss_acquire_cred_from", major, minor); + ++ if (replay) { ++ /* Induce a replay using cred as the acceptor cred, to test the replay ++ * cache indicated by the store. */ ++ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, name, ++ &mech_krb5, 0, GSS_C_INDEFINITE, ++ GSS_C_NO_CHANNEL_BINDINGS, ++ GSS_C_NO_BUFFER, NULL, &itok, NULL, NULL); ++ check_gsserr("gss_init_sec_context", major, minor); ++ (void)gss_delete_sec_context(&minor, &ictx, NULL); ++ ++ major = gss_accept_sec_context(&minor, &actx, cred, &itok, ++ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, ++ &atok, NULL, NULL, NULL); ++ check_gsserr("gss_accept_sec_context(1)", major, minor); ++ (void)gss_release_buffer(&minor, &atok); ++ (void)gss_delete_sec_context(&minor, &actx, NULL); ++ ++ major = gss_accept_sec_context(&minor, &actx, cred, &itok, ++ GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, ++ &atok, NULL, NULL, NULL); ++ check_gsserr("gss_accept_sec_context(2)", major, minor); ++ (void)gss_release_buffer(&minor, &atok); ++ (void)gss_delete_sec_context(&minor, &actx, NULL); ++ } ++ + gss_release_name(&minor, &name); + gss_release_cred(&minor, &cred); + free(store.elements); +diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py +index 74139e4..106910d 100755 +--- a/src/tests/gssapi/t_gssapi.py ++++ b/src/tests/gssapi/t_gssapi.py +@@ -89,11 +89,18 @@ + realm.addprinc(service_cs) + realm.extract_keytab(service_cs, servicekeytab) + realm.kinit(service_cs, None, ['-k', '-t', servicekeytab]) +-output = realm.run_as_client(['./t_credstore', service_cs, '--cred_store', ++output = realm.run_as_client(['./t_credstore', '-s', 'p:' + service_cs, + 'ccache', storagecache, 'keytab', servicekeytab]) +-if 'Cred Store Success' not in output: +- fail('Expected test to succeed') + ++# Test rcache feature of cred stores. t_credstore -r should produce a ++# replay error normally, but not with rcache set to "none:". ++output = realm.run_as_client(['./t_credstore', '-r', '-a', ++ 'p:' + realm.host_princ], expected_code=1) ++if 'gss_accept_sec_context(2): Request is a replay' not in output: ++ fail('Expected replay error not seen in t_credstore output') ++realm.run_as_client(['./t_credstore', '-r', '-a', 'p:' + realm.host_princ, ++ 'rcache', 'none:']) ++ + # Verify that we can't acquire acceptor creds without a keytab. + os.remove(realm.keytab) + output = realm.run_as_client(['./t_accname', 'p:abc'], expected_code=1) diff --git a/SOURCES/krb5-1.11-spnego-preserve-oid.patch b/SOURCES/krb5-1.11-spnego-preserve-oid.patch new file mode 100644 index 0000000..619a64c --- /dev/null +++ b/SOURCES/krb5-1.11-spnego-preserve-oid.patch @@ -0,0 +1,171 @@ +Adjusted to apply to 1.11.5. + +commit 8255613476d4c1583a5e810b50444f188fde871f +Author: Greg Hudson +Date: Mon Feb 3 21:11:34 2014 -0500 + + Properly reflect MS krb5 mech in SPNEGO acceptor + + r25590 changed negotiate_mech() to return an alias into the acceptor's + mech set, with the unfortunate side effect of transforming the + erroneous Microsoft krb5 mech OID into the correct krb5 mech OID, + meaning that we answer with a different OID than the requested one. + Return an alias into the initiator's mech set instead, and store that + in mech_set field the SPNEGO context. The acceptor code only uses + mech_set to hold the allocated storage pointed into by internal_mech, + so this change is safe. + + ticket: 7858 + target_version: 1.12.2 + tags: pullup + +diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c +index 7e4bf90..7529c74 100644 +--- a/src/lib/gssapi/spnego/spnego_mech.c ++++ b/src/lib/gssapi/spnego/spnego_mech.c +@@ -1388,8 +1388,8 @@ acc_ctx_new(OM_uint32 *minor_status, + *return_token = NO_TOKEN_SEND; + goto cleanup; + } +- sc->mech_set = supported_mechSet; +- supported_mechSet = GSS_C_NO_OID_SET; ++ sc->mech_set = mechTypes; ++ mechTypes = GSS_C_NO_OID_SET; + sc->internal_mech = mech_wanted; + sc->DER_mechTypes = der_mechTypes; + der_mechTypes.length = 0; +@@ -3538,7 +3538,7 @@ put_negResult(unsigned char **buf_out, OM_uint32 negResult, + * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if + * it's not the first mech, otherwise we return NULL and negResult + * is set to REJECT. The returned pointer is an alias into +- * supported->elements and should not be freed. ++ * received->elements and should not be freed. + * + * NOTE: There is currently no way to specify a preference order of + * mechanisms supported by the acceptor. +@@ -3560,7 +3560,7 @@ negotiate_mech(gss_OID_set supported, gss_OID_set received, + if (g_OID_equal(mech_oid, &supported->elements[j])) { + *negResult = (i == 0) ? ACCEPT_INCOMPLETE : + REQUEST_MIC; +- return &supported->elements[j]; ++ return &received->elements[i]; + } + } + } + +commit 53cfb8327c452bd72a8e915338fb5ec838079cd3 +Author: Greg Hudson +Date: Mon Feb 3 20:59:54 2014 -0500 + + Test SPNEGO acceptor response to MS krb5 mech OID + + In t_spnego.c, add code to make a SPNEGO request with the erroneous + Microsoft OID value and examine the response to make sure that it uses + the same OID value as the request did. The token and tmp variables + were unused, so rename them to itok and atok for the purpose of the + new test code. + + ticket: 7858 + target_version: 1.12.2 + tags: pullup + +diff --git a/src/tests/gssapi/t_spnego.c b/src/tests/gssapi/t_spnego.c +index cbf720b..ca05848 100644 +--- a/src/tests/gssapi/t_spnego.c ++++ b/src/tests/gssapi/t_spnego.c +@@ -27,9 +27,15 @@ + #include + #include + #include ++#include + + #include "common.h" + ++static gss_OID_desc mech_krb5_wrong = { ++ 9, "\052\206\110\202\367\022\001\002\002" ++}; ++gss_OID_set_desc mechset_krb5_wrong = { 1, &mech_krb5_wrong }; ++ + /* + * Test program for SPNEGO and gss_set_neg_mechs + * +@@ -44,13 +50,16 @@ main(int argc, char *argv[]) + { + OM_uint32 minor, major; + gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL; ++ gss_cred_id_t initiator_cred_handle = GSS_C_NO_CREDENTIAL; + gss_OID_set actual_mechs = GSS_C_NO_OID_SET; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER, tmp = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok = GSS_C_EMPTY_BUFFER; + gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; + gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT; + gss_name_t target_name, source_name = GSS_C_NO_NAME; + OM_uint32 time_rec; + gss_OID mech = GSS_C_NO_OID; ++ const unsigned char *atok_oid; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]); +@@ -83,10 +91,59 @@ main(int argc, char *argv[]) + + (void)gss_delete_sec_context(&minor, &acceptor_context, NULL); + (void)gss_release_name(&minor, &source_name); +- (void)gss_release_name(&minor, &target_name); +- (void)gss_release_buffer(&minor, &token); +- (void)gss_release_buffer(&minor, &tmp); + (void)gss_release_cred(&minor, &verifier_cred_handle); + (void)gss_release_oid_set(&minor, &actual_mechs); ++ ++ /* ++ * Test that the SPNEGO acceptor code properly reflects back the erroneous ++ * Microsoft mech OID in the supportedMech field of the NegTokenResp ++ * message. Our initiator code doesn't care (it treats all variants of the ++ * krb5 mech as equivalent when comparing the supportedMech response to its ++ * first-choice mech), so we have to look directly at the DER encoding of ++ * the response token. If we don't request mutual authentication, the ++ * SPNEGO reply will contain no underlying mech token, so the encoding of ++ * the correct NegotiationToken response is completely predictable: ++ * ++ * A1 14 (choice 1, length 20, meaning negTokenResp) ++ * 30 12 (sequence, length 18) ++ * A0 03 (context tag 0, length 3) ++ * 0A 01 00 (enumerated value 0, meaning accept-completed) ++ * A1 0B (context tag 1, length 11) ++ * 06 09 (object identifier, length 9) ++ * 2A 86 48 82 F7 12 01 02 02 (the erroneous krb5 OID) ++ * ++ * So we can just compare the length to 22 and the nine bytes at offset 13 ++ * to the expected OID. ++ */ ++ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, ++ &mechset_spnego, GSS_C_INITIATE, ++ &initiator_cred_handle, NULL, NULL); ++ check_gsserr("gss_acquire_cred(2)", major, minor); ++ major = gss_set_neg_mechs(&minor, initiator_cred_handle, ++ &mechset_krb5_wrong); ++ check_gsserr("gss_set_neg_mechs(2)", major, minor); ++ major = gss_init_sec_context(&minor, initiator_cred_handle, ++ &initiator_context, target_name, &mech_spnego, ++ GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, ++ GSS_C_INDEFINITE, ++ GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &itok, ++ NULL, NULL); ++ check_gsserr("gss_init_sec_context", major, minor); ++ assert(major == GSS_S_CONTINUE_NEEDED); ++ major = gss_accept_sec_context(&minor, &acceptor_context, ++ GSS_C_NO_CREDENTIAL, &itok, ++ GSS_C_NO_CHANNEL_BINDINGS, NULL, ++ NULL, &atok, NULL, NULL, NULL); ++ assert(atok.length == 22); ++ atok_oid = (unsigned char *)atok.value + 13; ++ assert(memcmp(atok_oid, mech_krb5_wrong.elements, 9) == 0); ++ check_gsserr("gss_accept_sec_context", major, minor); ++ ++ (void)gss_delete_sec_context(&minor, &initiator_context, NULL); ++ (void)gss_delete_sec_context(&minor, &acceptor_context, NULL); ++ (void)gss_release_cred(&minor, &initiator_cred_handle); ++ (void)gss_release_name(&minor, &target_name); ++ (void)gss_release_buffer(&minor, &itok); ++ (void)gss_release_buffer(&minor, &atok); + return 0; + } diff --git a/SOURCES/krb5-1.11.3-1.12.1-credstoretest.patch b/SOURCES/krb5-1.11.3-1.12.1-credstoretest.patch new file mode 100644 index 0000000..dbfd7fe --- /dev/null +++ b/SOURCES/krb5-1.11.3-1.12.1-credstoretest.patch @@ -0,0 +1,125 @@ +diff --git a/src/tests/gssapi/t_credstore.c b/src/tests/gssapi/t_credstore.c +index 085bc79..575f96d 100644 +--- a/src/tests/gssapi/t_credstore.c ++++ b/src/tests/gssapi/t_credstore.c +@@ -33,7 +33,7 @@ static void + usage(void) + { + fprintf(stderr, +- "Usage: t_credstore principal [--cred_store {key value} ...]\n"); ++ "Usage: t_credstore [-sabi] principal [{key value} ...]\n"); + exit(1); + } + +@@ -42,63 +42,66 @@ main(int argc, char *argv[]) + { + OM_uint32 minor, major; + gss_key_value_set_desc store; +- gss_buffer_desc buf; +- gss_name_t service = GSS_C_NO_NAME; ++ gss_name_t name; ++ gss_cred_usage_t cred_usage = GSS_C_BOTH; ++ gss_OID_set mechs = GSS_C_NO_OID_SET; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; +- int i, e; +- +- if (argc < 2 || ((argc - 3) % 2)) +- usage(); +- +- store.count = (argc - 3) / 2; +- store.elements = calloc(store.count, +- sizeof(struct gss_key_value_element_struct)); +- if (!store.elements) { +- fprintf(stderr, "OOM\n"); +- exit(1); ++ krb5_boolean store_creds = FALSE; ++ char opt; ++ ++ /* Parse options. */ ++ for (argv++; *argv != NULL && **argv == '-'; argv++) { ++ opt = (*argv)[1]; ++ if (opt == 's') ++ store_creds = TRUE; ++ else if (opt == 'a') ++ cred_usage = GSS_C_ACCEPT; ++ else if (opt == 'b') ++ cred_usage = GSS_C_BOTH; ++ else if (opt == 'i') ++ cred_usage = GSS_C_INITIATE; ++ else ++ usage(); + } + +- if (argc > 2) { +- if (strcmp(argv[2], "--cred_store") != 0) ++ /* Get the principal name. */ ++ if (*argv == NULL) ++ usage(); ++ name = import_name(*argv++); ++ ++ /* Put any remaining arguments into the store. */ ++ store.elements = calloc(argc, sizeof(struct gss_key_value_element_struct)); ++ if (!store.elements) ++ errout("OOM"); ++ store.count = 0; ++ while (*argv != NULL) { ++ if (*(argv + 1) == NULL) + usage(); +- +- for (i = 3, e = 0; i < argc; i += 2, e++) { +- store.elements[e].key = argv[i]; +- store.elements[e].value = argv[i + 1]; +- continue; +- } ++ store.elements[store.count].key = *argv; ++ store.elements[store.count].value = *(argv + 1); ++ store.count++; ++ argv += 2; + } + +- /* First acquire default creds and try to store them in the cred store. */ +- +- major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, +- GSS_C_INITIATE, &cred, NULL, NULL); +- check_gsserr("gss_acquire_cred", major, minor); ++ if (store_creds) { ++ /* Acquire default creds and try to store them in the cred store. */ ++ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, ++ GSS_C_INITIATE, &cred, NULL, NULL); ++ check_gsserr("gss_acquire_cred", major, minor); + +- major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, +- GSS_C_NO_OID, 1, 0, &store, NULL, NULL); +- check_gsserr("gss_store_cred_into", major, minor); ++ major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, ++ GSS_C_NO_OID, 1, 0, &store, NULL, NULL); ++ check_gsserr("gss_store_cred_into", major, minor); + +- gss_release_cred(&minor, &cred); +- +- /* Then try to acquire creds from store. */ +- +- buf.value = argv[1]; +- buf.length = strlen(argv[1]); +- +- major = gss_import_name(&minor, &buf, +- (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, +- &service); +- check_gsserr("gss_import_name", major, minor); ++ gss_release_cred(&minor, &cred); ++ } + +- major = gss_acquire_cred_from(&minor, service, +- 0, GSS_C_NO_OID_SET, GSS_C_BOTH, ++ /* Try to acquire creds from store. */ ++ major = gss_acquire_cred_from(&minor, name, 0, mechs, cred_usage, + &store, &cred, NULL, NULL); + check_gsserr("gss_acquire_cred_from", major, minor); + +- fprintf(stdout, "Cred Store Success\n"); +- +- gss_release_name(&minor, &service); ++ gss_release_name(&minor, &name); + gss_release_cred(&minor, &cred); + free(store.elements); + return 0; diff --git a/SOURCES/krb5-1.11.3-client-loop.patch b/SOURCES/krb5-1.11.3-client-loop.patch new file mode 100644 index 0000000..f9b7c13 --- /dev/null +++ b/SOURCES/krb5-1.11.3-client-loop.patch @@ -0,0 +1,34 @@ +Tweaked for 1.11.3. + +commit 53e5c850e05f011e9e7f25c2032aec51d8b352a9 +Author: Viktor Dukhovni +Date: Tue Jun 25 12:27:42 2013 -0400 + + Fix spin loop reading from KDC TCP socket + + In the k5_sendto code for reading from a TCP socket, detect + end-of-stream when reading the length. Otherwise we can get stuck in + an infinite loop of poll() and read(). + + [ghudson@mit.edu: commit message] + + ticket: 7508 + target_version: 1.11.4 + tags: pullup + +diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c +index 3e4ec7e..3c31d9f 100644 +--- a/src/lib/krb5/os/sendto_kdc.c ++++ b/src/lib/krb5/os/sendto_kdc.c +@@ -853,9 +853,9 @@ service_tcp_fd(krb5_context context, struct conn_state *conn, + nread = SOCKET_READ(conn->fd, + conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read, + 4 - conn->x.in.bufsizebytes_read); +- if (nread < 0) { ++ if (nread <= 0) { ++ e = nread ? SOCKET_ERRNO : ECONNRESET; + TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, conn, e); +- e = SOCKET_ERRNO; + goto kill_conn; + } + conn->x.in.bufsizebytes_read += nread; diff --git a/SOURCES/krb5-1.11.3-copy_context.patch b/SOURCES/krb5-1.11.3-copy_context.patch new file mode 100644 index 0000000..fbd35c4 --- /dev/null +++ b/SOURCES/krb5-1.11.3-copy_context.patch @@ -0,0 +1,302 @@ +Adjusted for 1.11.3, which still had vtbl, locate_fptrs, and (vestigial) +profile_in_memory fields, but didn't have localauth_handles, +hostrealm_handles, or dns_canonicalize_hostname, and drop the hunk that +touched .gitignore. + +commit c452644d91d57d8b05ef396a029e34d0c7a48920 +Author: Greg Hudson +Date: Wed Dec 18 15:03:03 2013 -0500 + + Fix krb5_copy_context + + krb5_copy_context has been broken since 1.8 (it broke in r22456) + because k5_copy_etypes crashes on null enctype lists. Subsequent + additions to the context structure were not reflected in + krb5_copy_context, creating double-free bugs. Make k5_copy_etypes + handle null input and account for all new fields in krb5_copy_context. + Reported by Arran Cudbard-Bell. + + ticket: 7807 (new) + target_version: 1.12.1 + tags: pullup + +diff --git a/src/lib/krb5/krb/copy_ctx.c b/src/lib/krb5/krb/copy_ctx.c +index 0bc92f8..4237023 100644 +--- a/src/lib/krb5/krb/copy_ctx.c ++++ b/src/lib/krb5/krb/copy_ctx.c +@@ -77,13 +77,24 @@ krb5_copy_context(krb5_context ctx, krb5_context *nctx_out) + nctx->ser_ctx_count = 0; + nctx->ser_ctx = NULL; + nctx->prompt_types = NULL; ++ nctx->preauth_context = NULL; ++ nctx->ccselect_handles = NULL; ++ nctx->kdblog_context = NULL; ++ nctx->trace_callback = NULL; ++ nctx->trace_callback_data = NULL; ++ nctx->plugin_base_dir = NULL; + nctx->os_context.default_ccname = NULL; + ++#ifdef KRB5_DNS_LOOKUP ++ nctx->profile_in_memory = 0; ++#endif /* KRB5_DNS_LOOKUP */ ++ + memset(&nctx->libkrb5_plugins, 0, sizeof(nctx->libkrb5_plugins)); + nctx->vtbl = NULL; + nctx->locate_fptrs = NULL; + + memset(&nctx->err, 0, sizeof(nctx->err)); ++ memset(&nctx->plugins, 0, sizeof(nctx->plugins)); + + ret = k5_copy_etypes(ctx->in_tkt_etypes, &nctx->in_tkt_etypes); + if (ret) +@@ -101,6 +109,11 @@ krb5_copy_context(krb5_context ctx, krb5_context *nctx_out) + ret = krb5_get_profile(ctx, &nctx->profile); + if (ret) + goto errout; ++ nctx->plugin_base_dir = strdup(ctx->plugin_base_dir); ++ if (nctx->plugin_base_dir == NULL) { ++ ret = ENOMEM; ++ goto errout; ++ } + + errout: + if (ret) { +diff --git a/src/lib/krb5/krb/etype_list.c b/src/lib/krb5/krb/etype_list.c +index 9efe2e0..71f664f 100644 +--- a/src/lib/krb5/krb/etype_list.c ++++ b/src/lib/krb5/krb/etype_list.c +@@ -49,6 +49,8 @@ k5_copy_etypes(const krb5_enctype *old_list, krb5_enctype **new_list) + krb5_enctype *list; + + *new_list = NULL; ++ if (old_list == NULL) ++ return 0; + count = k5_count_etypes(old_list); + list = malloc(sizeof(krb5_enctype) * (count + 1)); + if (list == NULL) + +commit b78c3c8c5025aec870d20472f80d4a652062f921 +Author: Greg Hudson +Date: Wed Dec 18 13:08:25 2013 -0500 + + Add a test program for krb5_copy_context + + This test program isn't completely proof against the kind of mistakes + we've made with krb5_copy_context in the past, but it at least + exercises krb5_copy_context and can detect some kinds of bugs. + + ticket: 7807 + +diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in +index 7d1682d..3b58219 100644 +--- a/src/lib/krb5/krb/Makefile.in ++++ b/src/lib/krb5/krb/Makefile.in +@@ -349,6 +349,7 @@ SRCS= $(srcdir)/addr_comp.c \ + $(srcdir)/t_expire_warn.c \ + $(srcdir)/t_authdata.c \ + $(srcdir)/t_cc_config.c \ ++ $(srcdir)/t_copy_context.c \ + $(srcdir)/t_in_ccache.c \ + $(srcdir)/t_response_items.c \ + $(srcdir)/t_vfy_increds.c +@@ -429,11 +430,14 @@ t_in_ccache: t_in_ccache.o $(KRB5_BASE_DEPLIBS) + t_cc_config: t_cc_config.o $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $@ t_cc_config.o $(KRB5_BASE_LIBS) + ++t_copy_context: t_copy_context.o $(KRB5_BASE_DEPLIBS) ++ $(CC_LINK) -o $@ t_copy_context.o $(KRB5_BASE_LIBS) ++ + t_response_items: t_response_items.o response_items.o $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $@ t_response_items.o response_items.o $(KRB5_BASE_LIBS) + + TEST_PROGS= t_walk_rtree t_kerb t_ser t_deltat t_expand t_authdata t_pac \ +- t_in_ccache t_cc_config \ ++ t_in_ccache t_cc_config t_copy_context \ + t_princ t_etypes t_vfy_increds t_response_items + + check-unix:: $(TEST_PROGS) +@@ -473,6 +477,8 @@ check-unix:: $(TEST_PROGS) + $(RUN_SETUP) $(VALGRIND) ./t_princ + $(RUN_SETUP) $(VALGRIND) ./t_etypes + $(RUN_SETUP) $(VALGRIND) ./t_response_items ++ KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\ ++ $(RUN_SETUP) $(VALGRIND) ./t_copy_context + + check-pytests:: t_expire_warn t_vfy_increds + $(RUNPYTEST) $(srcdir)/t_expire_warn.py $(PYTESTFLAGS) +@@ -491,6 +497,7 @@ clean:: + $(OUTPRE)t_pac$(EXEEXT) $(OUTPRE)t_pac.$(OBJEXT) \ + $(OUTPRE)t_princ$(EXEEXT) $(OUTPRE)t_princ.$(OBJEXT) \ + $(OUTPRE)t_authdata$(EXEEXT) $(OUTPRE)t_authdata.$(OBJEXT) \ ++ $(OUTPRE)t_copy_context(EXEEXT) $(OUTPRE)t_copy_context.$(OBJEXT) \ + $(OUTPRE)t_vfy_increds$(EXEEXT) $(OUTPRE)t_vfy_increds.$(OBJEXT) \ + $(OUTPRE)t_response_items$(EXEEXT) $(OUTPRE)t_response_items.$(OBJEXT) + +diff --git a/src/lib/krb5/krb/t_copy_context.c b/src/lib/krb5/krb/t_copy_context.c +new file mode 100644 +index 0000000..522fa0c +--- /dev/null ++++ b/src/lib/krb5/krb/t_copy_context.c +@@ -0,0 +1,162 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* lib/krb5/krb/t_copy_context.C - Test program for krb5_copy_context */ ++/* ++ * Copyright (C) 2013 by the Massachusetts Institute of Technology. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ++ * OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++ ++static void ++trace(krb5_context ctx, const krb5_trace_info *info, void *data) ++{ ++} ++ ++static void ++check(int cond) ++{ ++ if (!cond) ++ abort(); ++} ++ ++static void ++compare_string(const char *str1, const char *str2) ++{ ++ check((str1 == NULL) == (str2 == NULL)); ++ if (str1 != NULL) ++ check(strcmp(str1, str2) == 0); ++} ++ ++static void ++compare_etypes(krb5_enctype *list1, krb5_enctype *list2) ++{ ++ check((list1 == NULL) == (list2 == NULL)); ++ if (list1 == NULL) ++ return; ++ while (*list1 != ENCTYPE_NULL && *list1 == *list2) ++ list1++, list2++; ++ check(*list1 == *list2); ++} ++ ++/* Check that the context c is a valid copy of the reference context r. */ ++static void ++check_context(krb5_context c, krb5_context r) ++{ ++ int i; ++ ++ /* Check fields which should have been propagated from r. */ ++ compare_etypes(c->in_tkt_etypes, r->in_tkt_etypes); ++ compare_etypes(c->tgs_etypes, r->tgs_etypes); ++ check(c->os_context.time_offset == r->os_context.time_offset); ++ check(c->os_context.usec_offset == r->os_context.usec_offset); ++ check(c->os_context.os_flags == r->os_context.os_flags); ++ compare_string(c->os_context.default_ccname, r->os_context.default_ccname); ++ check(c->clockskew == r->clockskew); ++ check(c->kdc_req_sumtype == r->kdc_req_sumtype); ++ check(c->default_ap_req_sumtype == r->default_ap_req_sumtype); ++ check(c->default_safe_sumtype == r->default_safe_sumtype); ++ check(c->kdc_default_options == r->kdc_default_options); ++ check(c->library_options == r->library_options); ++ check(c->profile_secure == r->profile_secure); ++ check(c->fcc_default_format == r->fcc_default_format); ++ check(c->udp_pref_limit == r->udp_pref_limit); ++ check(c->use_conf_ktypes == r->use_conf_ktypes); ++ check(c->allow_weak_crypto == r->allow_weak_crypto); ++ check(c->ignore_acceptor_hostname == r->ignore_acceptor_hostname); ++ compare_string(c->plugin_base_dir, r->plugin_base_dir); ++ ++ /* Check fields which don't propagate. */ ++ check(c->dal_handle == NULL); ++ check(c->ser_ctx_count == 0); ++ check(c->ser_ctx == NULL); ++ check(c->prompt_types == NULL); ++ check(c->libkrb5_plugins.files == NULL); ++ check(c->preauth_context == NULL); ++ check(c->ccselect_handles == NULL); ++ check(c->err.code == 0); ++ check(c->err.msg == NULL); ++ check(c->kdblog_context == NULL); ++ check(c->trace_callback == NULL); ++ check(c->trace_callback_data == NULL); ++ for (i = 0; i < PLUGIN_NUM_INTERFACES; i++) { ++ check(c->plugins[i].modules == NULL); ++ check(!c->plugins[i].configured); ++ } ++} ++ ++int ++main(int argc, char **argv) ++{ ++ krb5_context ctx, ctx2; ++ krb5_plugin_initvt_fn *mods; ++ const krb5_enctype etypes1[] = { ENCTYPE_DES3_CBC_SHA1, 0 }; ++ const krb5_enctype etypes2[] = { ENCTYPE_AES128_CTS_HMAC_SHA1_96, ++ ENCTYPE_AES256_CTS_HMAC_SHA1_96, 0 }; ++ krb5_prompt_type ptypes[] = { KRB5_PROMPT_TYPE_PASSWORD }; ++ ++ /* Copy a default context and verify the result. */ ++ check(krb5_init_context(&ctx) == 0); ++ check(krb5_copy_context(ctx, &ctx2) == 0); ++ check_context(ctx2, ctx); ++ krb5_free_context(ctx2); ++ ++ /* Set non-default values for all of the propagated fields in ctx. */ ++ ctx->allow_weak_crypto = TRUE; ++ check(krb5_set_default_in_tkt_ktypes(ctx, etypes1) == 0); ++ check(krb5_set_default_tgs_enctypes(ctx, etypes2) == 0); ++ check(krb5_set_debugging_time(ctx, 1234, 5678) == 0); ++ check(krb5_cc_set_default_name(ctx, "defccname") == 0); ++ check(krb5_set_default_realm(ctx, "defrealm") == 0); ++ ctx->clockskew = 18; ++ ctx->kdc_req_sumtype = CKSUMTYPE_NIST_SHA; ++ ctx->default_ap_req_sumtype = CKSUMTYPE_HMAC_SHA1_96_AES128; ++ ctx->default_safe_sumtype = CKSUMTYPE_HMAC_SHA1_96_AES256; ++ ctx->kdc_default_options = KDC_OPT_FORWARDABLE; ++ ctx->library_options = 0; ++ ctx->profile_secure = TRUE; ++ ctx->udp_pref_limit = 2345; ++ ctx->use_conf_ktypes = TRUE; ++ ctx->ignore_acceptor_hostname = TRUE; ++ free(ctx->plugin_base_dir); ++ check((ctx->plugin_base_dir = strdup("/a/b/c/d")) != NULL); ++ ++ /* Also set some of the non-propagated fields. */ ++ ctx->prompt_types = ptypes; ++ check(k5_plugin_load_all(ctx, PLUGIN_INTERFACE_PWQUAL, &mods) == 0); ++ k5_plugin_free_modules(ctx, mods); ++ krb5_set_error_message(ctx, ENOMEM, "nooooooooo"); ++ krb5_set_trace_callback(ctx, trace, ctx); ++ ++ /* Copy the intentionally messy context and verify the result. */ ++ check(krb5_copy_context(ctx, &ctx2) == 0); ++ check_context(ctx2, ctx); ++ krb5_free_context(ctx2); ++ ++ krb5_free_context(ctx); ++ return 0; ++} diff --git a/SOURCES/krb5-1.11.3-nodelete-pkinit.patch b/SOURCES/krb5-1.11.3-nodelete-pkinit.patch new file mode 100644 index 0000000..a5892d6 --- /dev/null +++ b/SOURCES/krb5-1.11.3-nodelete-pkinit.patch @@ -0,0 +1,14 @@ +Cause the pkinit plugin to be kept in memory, so that it doesn't +reinitialize libcrypto every time it's loaded and subsequently lose +track of the memory that doing so allocates, every time the plugin is +unloaded. +--- krb5-1.11.3/src/plugins/preauth/pkinit/Makefile.in ++++ krb5-1.11.3/src/plugins/preauth/pkinit/Makefile.in +@@ -6,6 +6,7 @@ PROG_LIBPATH=-L$(TOPLIBD) + PROG_RPATH=$(KRB5_LIBDIR) + MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) + DEFS=@DEFS@ ++LDFLAGS += -Wl,-z,nodelete + + LOCALINCLUDES = -I../../../include/krb5 -I. $(PKINIT_CRYPTO_IMPL_CFLAGS) + RUN_SETUP = @KRB5_RUN_ENV@ diff --git a/SOURCES/krb5-1.11.3-otp2.patch b/SOURCES/krb5-1.11.3-otp2.patch new file mode 100644 index 0000000..da8d79a --- /dev/null +++ b/SOURCES/krb5-1.11.3-otp2.patch @@ -0,0 +1,699 @@ +A couple of other changes that were made for OTP support when it was merged, +extracted and applied after the OTP backport: +* Read the secret that we share with the RADIUS server from the file named by + the "secret" configuration setting. +* Log an error if we can't parse the OTP configuration string for a given + principal entry. +* Pass back the error code if attempting to read the OTP configuration string + for an entry produces one. +* When setting the remote address on the socket we're using to talk to the + RADIUS server, wait for the non-blocking connect() to complete by waiting + for it to become writable instead of readable. +--- krb5-1.11.3/src/include/k5-int.h ++++ krb5-1.11.3/src/include/k5-int.h +@@ -2612,6 +2612,17 @@ k5memdup(const void *in, size_t len, krb + return ptr; + } + ++/* Like k5memdup, but add a final null byte. */ ++static inline void * ++k5memdup0(const void *in, size_t len, krb5_error_code *code) ++{ ++ void *ptr = k5alloc(len + 1, code); ++ ++ if (ptr != NULL) ++ memcpy(ptr, in, len); ++ return ptr; ++} ++ + krb5_error_code KRB5_CALLCONV + krb5_get_credentials_for_user(krb5_context context, krb5_flags options, + krb5_ccache ccache, +--- krb5-1.11.3/src/plugins/preauth/otp/otp_state.c ++++ krb5-1.11.3/src/plugins/preauth/otp/otp_state.c +@@ -43,6 +43,7 @@ + #define DEFAULT_SOCKET_FMT KDC_DIR "/%s.socket" + #define DEFAULT_TIMEOUT 5 + #define DEFAULT_RETRIES 3 ++#define MAX_SECRET_LEN 1024 + + typedef struct token_type_st { + char *name; +@@ -76,6 +77,52 @@ struct otp_state_st { + + static void request_send(request *req); + ++static krb5_error_code ++read_secret_file(const char *secret_file, char **secret) ++{ ++ char buf[MAX_SECRET_LEN]; ++ krb5_error_code retval; ++ char *filename; ++ FILE *file; ++ int i, j; ++ ++ *secret = NULL; ++ ++ retval = k5_path_join(KDC_DIR, secret_file, &filename); ++ if (retval != 0) { ++ com_err("otp", retval, "Unable to resolve secret file '%s'", filename); ++ return retval; ++ } ++ ++ file = fopen(filename, "r"); ++ if (file == NULL) { ++ retval = errno; ++ com_err("otp", retval, "Unable to open secret file '%s'", filename); ++ return retval; ++ } ++ ++ if (fgets(buf, sizeof(buf), file) == NULL) ++ retval = EIO; ++ fclose(file); ++ if (retval != 0) { ++ com_err("otp", retval, "Unable to read secret file '%s'", filename); ++ return retval; ++ } ++ ++ /* Strip whitespace. */ ++ for (i = 0; buf[i] != '\0'; i++) { ++ if (!isspace(buf[i])) ++ break; ++ } ++ for (j = strlen(buf) - i; j > 0; j--) { ++ if (!isspace(buf[j - 1])) ++ break; ++ } ++ ++ *secret = k5memdup0(&buf[i], j - i, &retval); ++ return retval; ++} ++ + /* Free the contents of a single token type. */ + static void + token_type_free(token_type *type) +@@ -125,8 +172,7 @@ static krb5_error_code + token_type_decode(profile_t profile, const char *name, token_type *out) + { + krb5_error_code retval; +- char *server = NULL, *name_copy = NULL, *secret = NULL; +- const char *default_secret; ++ char *server = NULL, *name_copy = NULL, *secret = NULL, *pstr = NULL; + int strip_realm, timeout, retries; + + memset(out, 0, sizeof(*out)); +@@ -152,16 +198,27 @@ token_type_decode(profile_t profile, con + } + + /* Get the secret (optional for Unix-domain sockets). */ +- default_secret = (*server == '/') ? "" : NULL; +- retval = profile_get_string(profile, "otp", name, "secret", default_secret, +- &secret); ++ retval = profile_get_string(profile, "otp", name, "secret", NULL, &pstr); + if (retval != 0) + goto cleanup; +- if (secret == NULL) { +- com_err("otp", EINVAL, "Secret not specified in token type '%s'", +- name); +- retval = EINVAL; +- goto cleanup; ++ if (pstr != NULL) { ++ retval = read_secret_file(pstr, &secret); ++ profile_release_string(pstr); ++ if (retval != 0) ++ goto cleanup; ++ } else { ++ if (server[0] != '/') { ++ com_err("otp", EINVAL, "Secret missing (token type '%s')", name); ++ retval = EINVAL; ++ goto cleanup; ++ } ++ ++ /* Use the default empty secret for UNIX domain stream sockets. */ ++ secret = strdup(""); ++ if (secret == NULL) { ++ retval = ENOMEM; ++ goto cleanup; ++ } + } + + /* Get the timeout (profile value in seconds, result in milliseconds). */ +@@ -521,6 +578,7 @@ otp_state_verify(otp_state *state, verto + { + krb5_error_code retval; + request *rqst = NULL; ++ char *name; + + if (state->radius == NULL) { + retval = krad_client_new(state->ctx, ctx, &state->radius); +@@ -548,8 +606,14 @@ otp_state_verify(otp_state *state, verto + + retval = tokens_decode(state->ctx, princ, state->types, config, + &rqst->tokens); +- if (retval != 0) ++ if (retval != 0) { ++ if (krb5_unparse_name(state->ctx, princ, &name) == 0) { ++ com_err("otp", retval, ++ "Can't decode otp config string for principal '%s'", name); ++ krb5_free_unparsed_name(state->ctx, name); ++ } + goto error; ++ } + + request_send(rqst); + return; +--- krb5-1.11.3/src/plugins/preauth/otp/main.c ++++ krb5-1.11.3/src/plugins/preauth/otp/main.c +@@ -204,7 +204,9 @@ otp_edata(krb5_context context, krb5_kdc + + /* Determine if otp is enabled for the user. */ + retval = cb->get_string(context, rock, "otp", &config); +- if (retval != 0 || config == NULL) ++ if (retval == 0 && config == NULL) ++ retval = ENOENT; ++ if (retval != 0) + goto out; + cb->free_string(context, rock, config); + +@@ -305,7 +307,7 @@ otp_verify(krb5_context context, krb5_da + + /* Get the principal's OTP configuration string. */ + retval = cb->get_string(context, rock, "otp", &config); +- if (config == NULL) ++ if (retval == 0 && config == NULL) + retval = KRB5_PREAUTH_FAILED; + if (retval != 0) { + free(rs); +--- krb5-1.11.3/src/lib/krad/remote.c ++++ krb5-1.11.3/src/lib/krad/remote.c +@@ -36,9 +36,10 @@ + + #include + +-#define FLAGS_READ (VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_CLOSE_FD | \ +- VERTO_EV_FLAG_IO_ERROR | VERTO_EV_FLAG_IO_READ) +-#define FLAGS_WRITE (FLAGS_READ | VERTO_EV_FLAG_IO_WRITE) ++#define FLAGS_NONE VERTO_EV_FLAG_NONE ++#define FLAGS_READ VERTO_EV_FLAG_IO_READ ++#define FLAGS_WRITE VERTO_EV_FLAG_IO_WRITE ++#define FLAGS_BASE VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_ERROR + + TAILQ_HEAD(request_head, request_st); + +@@ -58,6 +59,7 @@ + struct krad_remote_st { + krb5_context kctx; + verto_ctx *vctx; ++ int fd; + verto_ev *io; + char *secret; + struct addrinfo *info; +@@ -69,6 +71,9 @@ + static void + on_io(verto_ctx *ctx, verto_ev *ev); + ++static void ++on_timeout(verto_ctx *ctx, verto_ev *ev); ++ + /* Iterate over the set of outstanding packets. */ + static const krad_packet * + iterator(request **out) +@@ -121,91 +126,131 @@ + } + } + +-/* Handle when packets receive no response within their alloted time. */ +-static void +-on_timeout(verto_ctx *ctx, verto_ev *ev) ++/* Start the timeout timer for the request. */ ++static krb5_error_code ++request_start_timer(request *r, verto_ctx *vctx) + { +- request *req = verto_get_private(ev); ++ verto_del(r->timer); + +- req->timer = NULL; /* Void the timer event. */ ++ r->timer = verto_add_timeout(vctx, VERTO_EV_FLAG_NONE, on_timeout, ++ r->timeout); ++ if (r->timer != NULL) ++ verto_set_private(r->timer, r, NULL); + +- /* If we have more retries to perform, resend the packet. */ +- if (req->retries-- > 1) { +- req->sent = 0; +- verto_set_flags(req->rr->io, FLAGS_WRITE); +- return; +- } ++ return (r->timer == NULL) ? ENOMEM : 0; ++} + +- request_finish(req, ETIMEDOUT, NULL); ++/* Disconnect from the remote host. */ ++static void ++remote_disconnect(krad_remote *rr) ++{ ++ close(rr->fd); ++ verto_del(rr->io); ++ rr->fd = -1; ++ rr->io = NULL; + } + +-/* Connect to the remote host. */ ++/* Add the specified flags to the remote. This automatically manages the ++ * lifecyle of the underlying event. Also connects if disconnected. */ + static krb5_error_code +-remote_connect(krad_remote *rr) ++remote_add_flags(krad_remote *remote, verto_ev_flag flags) + { +- int i, sock = -1; +- verto_ev *ev; ++ verto_ev_flag curflags = VERTO_EV_FLAG_NONE; ++ int i; + +- sock = socket(rr->info->ai_family, rr->info->ai_socktype, +- rr->info->ai_protocol); +- if (sock < 0) +- return errno; ++ flags &= (FLAGS_READ | FLAGS_WRITE); ++ if (remote == NULL || flags == FLAGS_NONE) ++ return EINVAL; ++ ++ /* If there is no connection, connect. */ ++ if (remote->fd < 0) { ++ verto_del(remote->io); ++ remote->io = NULL; ++ ++ remote->fd = socket(remote->info->ai_family, remote->info->ai_socktype, ++ remote->info->ai_protocol); ++ if (remote->fd < 0) ++ return errno; + +- i = connect(sock, rr->info->ai_addr, rr->info->ai_addrlen); +- if (i < 0) { +- i = errno; +- close(sock); +- return i; ++ i = connect(remote->fd, remote->info->ai_addr, ++ remote->info->ai_addrlen); ++ if (i < 0) { ++ i = errno; ++ remote_disconnect(remote); ++ return i; ++ } + } + +- ev = verto_add_io(rr->vctx, FLAGS_READ, on_io, sock); +- if (ev == NULL) { +- close(sock); +- return ENOMEM; ++ if (remote->io == NULL) { ++ remote->io = verto_add_io(remote->vctx, FLAGS_BASE | flags, ++ on_io, remote->fd); ++ if (remote->io == NULL) ++ return ENOMEM; ++ verto_set_private(remote->io, remote, NULL); + } + +- rr->io = ev; +- verto_set_private(rr->io, rr, NULL); ++ curflags = verto_get_flags(remote->io); ++ if ((curflags & flags) != flags) ++ verto_set_flags(remote->io, FLAGS_BASE | curflags | flags); ++ + return 0; + } + +-/* Disconnect and reconnect to the remote host. */ +-static krb5_error_code +-remote_reconnect(krad_remote *rr, int errnum) ++/* Remove the specified flags to the remote. This automatically manages the ++ * lifecyle of the underlying event. */ ++static void ++remote_del_flags(krad_remote *remote, verto_ev_flag flags) ++{ ++ if (remote == NULL || remote->io == NULL) ++ return; ++ ++ flags = verto_get_flags(remote->io) & (FLAGS_READ | FLAGS_WRITE) & ~flags; ++ if (flags == FLAGS_NONE) { ++ verto_del(remote->io); ++ remote->io = NULL; ++ return; ++ } ++ ++ verto_set_flags(remote->io, FLAGS_BASE | flags); ++} ++ ++/* Close the connection and start the timers of all outstanding requests. */ ++static void ++remote_shutdown(krad_remote *rr) + { + krb5_error_code retval; +- const krb5_data *tmp; + request *r; + +- verto_del(rr->io); +- rr->io = NULL; +- retval = remote_connect(rr); +- if (retval != 0) +- return retval; ++ remote_disconnect(rr); + ++ /* Start timers for all unsent packets. */ + TAILQ_FOREACH(r, &rr->list, list) { +- tmp = krad_packet_encode(r->request); +- +- if (r->sent == tmp->length) { +- /* Error out sent requests. */ +- request_finish(r, errnum, NULL); +- } else { +- /* Reset partially sent requests. */ +- r->sent = 0; ++ if (r->timer == NULL) { ++ retval = request_start_timer(r, rr->vctx); ++ if (retval != 0) ++ request_finish(r, retval, NULL); + } + } +- +- return 0; + } + +-/* Close the connection and call the callbacks of all oustanding requests. */ ++/* Handle when packets receive no response within their alloted time. */ + static void +-remote_shutdown(krad_remote *rr, int errnum) ++on_timeout(verto_ctx *ctx, verto_ev *ev) + { +- verto_del(rr->io); +- rr->io = NULL; +- while (!TAILQ_EMPTY(&rr->list)) +- request_finish(TAILQ_FIRST(&rr->list), errnum, NULL); ++ request *req = verto_get_private(ev); ++ krb5_error_code retval = ETIMEDOUT; ++ ++ req->timer = NULL; /* Void the timer event. */ ++ ++ /* If we have more retries to perform, resend the packet. */ ++ if (req->retries-- > 1) { ++ req->sent = 0; ++ retval = remote_add_flags(req->rr, FLAGS_WRITE); ++ if (retval == 0) ++ return; ++ } ++ ++ request_finish(req, retval, NULL); + } + + /* Write data to the socket. */ +@@ -213,8 +258,8 @@ + on_io_write(krad_remote *rr) + { + const krb5_data *tmp; ++ ssize_t written; + request *r; +- int i; + + TAILQ_FOREACH(r, &rr->list, list) { + tmp = krad_packet_encode(r->request); +@@ -224,45 +269,38 @@ + continue; + + /* Send the packet. */ +- i = sendto(verto_get_fd(rr->io), tmp->data + r->sent, +- tmp->length - r->sent, 0, NULL, 0); +- if (i < 0) { ++ written = sendto(verto_get_fd(rr->io), tmp->data + r->sent, ++ tmp->length - r->sent, 0, NULL, 0); ++ if (written < 0) { + /* Should we try again? */ + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS || + errno == EINTR) + return; + +- /* In this case, we need to re-connect. */ +- i = remote_reconnect(rr, errno); +- if (i == 0) +- return; +- +- /* Do a full reset. */ +- remote_shutdown(rr, i); ++ /* This error can't be worked around. */ ++ remote_shutdown(rr); + return; + } + +- /* SOCK_STREAM permits partial writes. */ +- if (rr->info->ai_socktype == SOCK_STREAM) +- r->sent += i; +- else if (i == (int)tmp->length) +- r->sent = i; +- + /* If the packet was completely sent, set a timeout. */ ++ r->sent += written; + if (r->sent == tmp->length) { +- verto_del(r->timer); +- r->timer = verto_add_timeout(rr->vctx, VERTO_EV_FLAG_NONE, +- on_timeout, r->timeout); +- if (r->timer == NULL) ++ if (request_start_timer(r, rr->vctx) != 0) { + request_finish(r, ENOMEM, NULL); +- else +- verto_set_private(r->timer, r, NULL); ++ return; ++ } ++ ++ if (remote_add_flags(rr, FLAGS_READ) != 0) { ++ remote_shutdown(rr); ++ return; ++ } + } + + return; + } + +- verto_set_flags(rr->io, FLAGS_READ); ++ remote_del_flags(rr, FLAGS_WRITE); ++ return; + } + + /* Read data from the socket. */ +@@ -280,9 +318,9 @@ + if (rr->info->ai_socktype == SOCK_STREAM) { + pktlen = krad_packet_bytes_needed(&rr->buffer); + if (pktlen < 0) { +- retval = remote_reconnect(rr, EBADMSG); +- if (retval != 0) +- remote_shutdown(rr, retval); ++ /* If we received a malformed packet on a stream socket, ++ * assume the socket to be unrecoverable. */ ++ remote_shutdown(rr); + return; + } + } +@@ -295,26 +333,11 @@ + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) + return; + +- if (errno == ECONNREFUSED || errno == ECONNRESET || +- errno == ENOTCONN) { +- /* +- * When doing UDP against a local socket, the kernel will notify +- * when the daemon closes. But not against remote sockets. We want +- * to treat them both the same. Returning here will cause an +- * eventual timeout. +- */ +- if (rr->info->ai_socktype != SOCK_STREAM) +- return; +- } +- +- /* In this case, we need to re-connect. */ +- i = remote_reconnect(rr, errno); +- if (i == 0) +- return; +- +- /* Do a full reset. */ +- remote_shutdown(rr, i); ++ /* The socket is unrecoverable. */ ++ remote_shutdown(rr); + return; ++ } else if (i == 0) { ++ remote_del_flags(rr, FLAGS_READ); + } + + /* If we have a partial read or just the header, try again. */ +@@ -374,6 +397,7 @@ + tmp->vctx = vctx; + tmp->buffer = make_data(tmp->buffer_, 0); + TAILQ_INIT(&tmp->list); ++ tmp->fd = -1; + + tmp->secret = strdup(secret); + if (tmp->secret == NULL) +@@ -389,10 +413,6 @@ + tmp->info->ai_next = NULL; + tmp->info->ai_canonname = NULL; + +- retval = remote_connect(tmp); +- if (retval != 0) +- goto error; +- + *rr = tmp; + return 0; + +@@ -414,7 +434,7 @@ + if (rr->info != NULL) + free(rr->info->ai_addr); + free(rr->info); +- verto_del(rr->io); ++ remote_disconnect(rr); + free(rr); + } + +@@ -440,21 +460,14 @@ + } + } + +- if (rr->io == NULL) { +- retval = remote_connect(rr); +- if (retval != 0) +- goto error; +- } +- +- if (rr->info->ai_socktype == SOCK_STREAM) +- retries = 0; + timeout = timeout / (retries + 1); + retval = request_new(rr, tmp, timeout, retries, cb, data, &r); + if (retval != 0) + goto error; + +- if ((verto_get_flags(rr->io) & VERTO_EV_FLAG_IO_WRITE) == 0) +- verto_set_flags(rr->io, FLAGS_WRITE); ++ retval = remote_add_flags(rr, FLAGS_WRITE); ++ if (retval != 0) ++ goto error; + + TAILQ_INSERT_TAIL(&rr->list, r, list); + if (pkt != NULL) +--- krb5-1.11.3/src/lib/krad/t_attr.c ++++ krb5-1.11.3/src/lib/krad/t_attr.c +@@ -29,7 +29,7 @@ + + #include "t_test.h" + +-const static char encoded[] = { ++const static unsigned char encoded[] = { + 0xba, 0xfc, 0xed, 0x50, 0xe1, 0xeb, 0xa6, 0xc3, + 0xc1, 0x75, 0x20, 0xe9, 0x10, 0xce, 0xc2, 0xcb + }; +--- krb5-1.11.3/src/lib/krad/t_attrset.c ++++ krb5-1.11.3/src/lib/krad/t_attrset.c +@@ -34,7 +34,7 @@ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +-const static char encpass[] = { ++const static unsigned char encpass[] = { + 0x58, 0x8d, 0xff, 0xda, 0x37, 0xf9, 0xe4, 0xca, + 0x19, 0xae, 0x49, 0xb7, 0x16, 0x6d, 0x58, 0x27 + }; +--- krb5-1.11.3/src/lib/krad/t_daemon.h ++++ krb5-1.11.3/src/lib/krad/t_daemon.h +@@ -51,8 +51,8 @@ + static krb5_boolean + daemon_start(int argc, const char **argv) + { +- sigset_t set; +- int sig; ++ int fds[2]; ++ char buf[1]; + + if (argc != 3 || argv == NULL) + return FALSE; +@@ -60,30 +60,23 @@ + if (daemon_pid != 0) + return TRUE; + +- if (sigemptyset(&set) != 0) +- return FALSE; +- +- if (sigaddset(&set, SIGUSR1) != 0) +- return FALSE; +- +- if (sigaddset(&set, SIGCHLD) != 0) +- return FALSE; +- +- if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) ++ if (pipe(fds) != 0) + return FALSE; + ++ /* Start the child process with the write end of the pipe as stdout. */ + daemon_pid = fork(); + if (daemon_pid == 0) { +- close(STDOUT_FILENO); +- open("/dev/null", O_WRONLY); ++ dup2(fds[1], STDOUT_FILENO); ++ close(fds[0]); ++ close(fds[1]); + exit(execlp(argv[1], argv[1], argv[2], NULL)); + } ++ close(fds[1]); + +- if (sigwait(&set, &sig) != 0 || sig == SIGCHLD) { +- daemon_stop(); +- daemon_pid = 0; ++ /* The child will write a sentinel character when it is listening. */ ++ if (read(fds[0], buf, 1) != 1 || *buf != '~') + return FALSE; +- } ++ close(fds[0]); + + atexit(daemon_stop); + return TRUE; +--- krb5-1.11.3/src/lib/krad/t_daemon.py ++++ krb5-1.11.3/src/lib/krad/t_daemon.py +@@ -33,7 +33,7 @@ + try: + from pyrad import dictionary, packet, server + except ImportError: +- sys.stdout.write("pyrad not found!\n") ++ sys.stderr.write("pyrad not found!\n") + sys.exit(0) + + # We could use a dictionary file, but since we need +@@ -49,28 +49,25 @@ + server.Server._HandleAuthPacket(self, pkt) + + passwd = [] +- +- print "Request: " ++ + for key in pkt.keys(): + if key == "User-Password": + passwd = map(pkt.PwDecrypt, pkt[key]) +- print "\t%s\t%s" % (key, passwd) +- else: +- print "\t%s\t%s" % (key, pkt[key]) +- ++ + reply = self.CreateReplyPacket(pkt) + if passwd == ['accept']: + reply.code = packet.AccessAccept +- print "Response: %s" % "Access-Accept" + else: + reply.code = packet.AccessReject +- print "Response: %s" % "Access-Reject" +- print + self.SendReplyPacket(pkt.fd, reply) + + srv = TestServer(addresses=["localhost"], + hosts={"127.0.0.1": + server.RemoteHost("127.0.0.1", "foo", "localhost")}, + dict=dictionary.Dictionary(StringIO.StringIO(DICTIONARY))) +-os.kill(os.getppid(), signal.SIGUSR1) ++ ++# Write a sentinel character to let the parent process know we're listening. ++sys.stdout.write("~") ++sys.stdout.flush() ++ + srv.Run() diff --git a/SOURCES/krb5-1.11.5-move-otp-sockets.patch b/SOURCES/krb5-1.11.5-move-otp-sockets.patch new file mode 100644 index 0000000..14a2355 --- /dev/null +++ b/SOURCES/krb5-1.11.5-move-otp-sockets.patch @@ -0,0 +1,180 @@ +Adjusted to apply after the local doublelog patch, and to avoid adding +changes to otp.rst, which wasn't added by the original backports. + +commit 1e4bdcfed2c7bda94d5c135cc32a5993ca032501 +Author: Nathaniel McCallum +Date: Wed Feb 5 10:59:46 2014 -0500 + + Move OTP sockets to KDC_RUN_DIR + + Some system configurations expect Unix-domain sockets to live under + /run or /var/run, and not other parts of /var where persistent + application state lives. Define a new directory KDC_RUN_DIR using + $runstatedir (new in autoconf 2.70, so fall back to $localstatedir/run + if it's not set) and use that for the default socket path. + + [ghudson@mit.edu: commit message, otp.rst formatting fix] + + ticket: 7859 (new) + +diff --git a/doc/conf.py b/doc/conf.py +index f015fc8..bc8b2bd 100644 +--- a/doc/conf.py ++++ b/doc/conf.py +@@ -231,6 +231,7 @@ if 'mansubs' in tags: + sbindir = '``@SBINDIR@``' + libdir = '``@LIBDIR@``' + localstatedir = '``@LOCALSTATEDIR@``' ++ runstatedir = '``@RUNSTATEDIR@``' + sysconfdir = '``@SYSCONFDIR@``' + ccache = '``@CCNAME@``' + keytab = '``@KTNAME@``' +@@ -243,6 +244,7 @@ else: + sbindir = ':ref:`SBINDIR `' + libdir = ':ref:`LIBDIR `' + localstatedir = ':ref:`LOCALSTATEDIR `' ++ runstatedir = ':ref:`RUNSTATEDIR `' + sysconfdir = ':ref:`SYSCONFDIR `' + ccache = ':ref:`DEFCCNAME `' + keytab = ':ref:`DEFKTNAME `' +@@ -262,6 +264,7 @@ else: + rst_epilog += '.. |sbindir| replace:: %s\n' % sbindir + rst_epilog += '.. |libdir| replace:: %s\n' % libdir + rst_epilog += '.. |kdcdir| replace:: %s\\ ``/krb5kdc``\n' % localstatedir ++ rst_epilog += '.. |kdcrundir| replace:: %s\\ ``/krb5kdc``\n' % runstatedir + rst_epilog += '.. |sysconfdir| replace:: %s\n' % sysconfdir + rst_epilog += '.. |ccache| replace:: %s\n' % ccache + rst_epilog += '.. |keytab| replace:: %s\n' % keytab +diff --git a/doc/mitK5defaults.rst b/doc/mitK5defaults.rst +index 89b8f4c..838dabb 100644 +--- a/doc/mitK5defaults.rst ++++ b/doc/mitK5defaults.rst +@@ -17,6 +17,7 @@ KDC config file :ref:`kdc.conf(5)` |kdcdir|\ ``/kdc.conf`` **KRB + KDC database path (DB2) |kdcdir|\ ``/principal`` + Master key :ref:`stash_definition` |kdcdir|\ ``/.k5.``\ *realm* + Admin server ACL file :ref:`kadm5.acl(5)` |kdcdir|\ ``/kadm5.acl`` ++OTP socket directory |kdcrundir| + Plugin base directory |libdir|\ ``/krb5/plugins`` + :ref:`rcache_definition` directory ``/var/tmp`` **KRB5RCACHEDIR** + Master key default enctype |defmkey| +@@ -64,6 +65,7 @@ Description Symbolic name Custom build path Typical + User programs BINDIR ``/usr/local/bin`` ``/usr/bin`` + Libraries and plugins LIBDIR ``/usr/local/lib`` ``/usr/lib`` + Parent of KDC state dir LOCALSTATEDIR ``/usr/local/var`` ``/var`` ++Parent of KDC runtime dir RUNSTATEDIR ``/usr/local/var/run`` ``/run`` + Administrative programs SBINDIR ``/usr/local/sbin`` ``/usr/sbin`` + Alternate krb5.conf dir SYSCONFDIR ``/usr/local/etc`` ``/etc`` + Default ccache name DEFCCNAME ``FILE:/tmp/krb5cc_%{uid}`` ``FILE:/tmp/krb5cc_%{uid}`` +diff --git a/src/Makefile.in b/src/Makefile.in +index a8bc990..1725093 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -64,6 +64,7 @@ INSTALLMKDIRS = $(KRB5ROOT) $(KRB5MANROOT) $(KRB5OTHERMKDIRS) \ + $(KRB5_AD_MODULE_DIR) \ + $(KRB5_LIBKRB5_MODULE_DIR) \ + @localstatedir@ @localstatedir@/krb5kdc \ ++ @runstatedir@ @runstatedir@/krb5kdc \ + $(KRB5_INCSUBDIRS) $(datadir) $(EXAMPLEDIR) + + install-strip: +diff --git a/src/configure.in b/src/configure.in +index 2145d54..c2eaf78 100644 +--- a/src/configure.in ++++ b/src/configure.in +@@ -1,5 +1,11 @@ + K5_AC_INIT([aclocal.m4]) + ++# If $runstatedir isn't set by autoconf (<2.70), set it manually. ++if test x"$runstatedir" == x; then ++ runstatedir=$localstatedir/run ++fi ++AC_SUBST(runstatedir) ++ + CONFIG_RULES + KRB5_VERSION=K5_VERSION + AC_SUBST(KRB5_VERSION) +diff --git a/src/doc/Makefile.in b/src/doc/Makefile.in +index a6bb7c5..b07e16a 100644 +--- a/src/doc/Makefile.in ++++ b/src/doc/Makefile.in +@@ -7,6 +7,7 @@ DOXYGEN=doxygen + + docsrc=$(top_srcdir)/../doc + localstatedir=@localstatedir@ ++runstatedir=@runstatedir@ + sysconfdir=@sysconfdir@ + DEFCCNAME=@DEFCCNAME@ + DEFKTNAME=@DEFKTNAME@ +@@ -113,6 +114,7 @@ paths.py: + echo 'sbindir = "``$(SERVER_BINDIR)``"' >> $@ + echo 'libdir = "``$(KRB5_LIBDIR)``"' >> $@ + echo 'localstatedir = "``$(localstatedir)``"' >> $@ ++ echo 'runstatedir = "``$(runstatedir)``"' >> $@ + echo 'sysconfdir = "``$(sysconfdir)``"' >> $@ + echo 'ccache = "``$(DEFCCNAME)``"' >> $@ + echo 'keytab = "``$(DEFKTNAME)``"' >> $@ +diff --git a/src/include/Makefile.in b/src/include/Makefile.in +index e13042a..f83ff4e 100644 +--- a/src/include/Makefile.in ++++ b/src/include/Makefile.in +@@ -53,6 +53,7 @@ autoconf.stamp: $(srcdir)/autoconf.h.in $(BUILDTOP)/config.status + + SYSCONFDIR = @sysconfdir@ + LOCALSTATEDIR = @localstatedir@ ++RUNSTATEDIR = @runstatedir@ + BINDIR = @bindir@ + SBINDIR = @sbindir@ + LIBDIR = @libdir@ +@@ -66,6 +67,7 @@ PROCESS_REPLACE = -e "s+@KRB5RCTMPDIR+$(KRB5RCTMPDIR)+" \ + -e "s+@MODULEDIR+$(MODULE_DIR)+" \ + -e "s+@GSSMODULEDIR+$(GSS_MODULE_DIR)+" \ + -e 's+@LOCALSTATEDIR+$(LOCALSTATEDIR)+' \ ++ -e 's+@RUNSTATEDIR+$(RUNSTATEDIR)+' \ + -e 's+@SYSCONFDIR+$(SYSCONFDIR)+' \ + -e 's+:/etc/krb5.conf:/etc/krb5.conf"+:/etc/krb5.conf"+' \ + -e 's+"/etc/krb5.conf:/etc/krb5.conf"+"/etc/krb5.conf"+' \ +diff --git a/src/include/osconf.hin b/src/include/osconf.hin +index 90ab86d..871503a 100644 +--- a/src/include/osconf.hin ++++ b/src/include/osconf.hin +@@ -59,6 +59,7 @@ + #define PLUGIN_EXT "@DYNOBJEXT" + + #define KDC_DIR "@LOCALSTATEDIR/krb5kdc" ++#define KDC_RUN_DIR "@RUNSTATEDIR/krb5kdc" + #define DEFAULT_KDB_FILE KDC_DIR "/principal" + #define DEFAULT_KEYFILE_STUB KDC_DIR "/.k5." + #define KRB5_DEFAULT_ADMIN_ACL KDC_DIR "/krb5_adm.acl" +diff --git a/src/man/Makefile.in b/src/man/Makefile.in +index 4dd2448..2b9c892 100644 +--- a/src/man/Makefile.in ++++ b/src/man/Makefile.in +@@ -5,6 +5,7 @@ SPHINX_BUILD=sphinx-build + GROFF=@GROFF@ + GROFF_MAN=$(GROFF) -mtty-char -Tascii -mandoc -c + localstatedir=@localstatedir@ ++runstatedir=@runstatedir@ + sysconfdir=@sysconfdir@ + DEFCCNAME=@DEFCCNAME@ + DEFKTNAME=@DEFKTNAME@ +@@ -44,6 +45,7 @@ $(docsrc)/version.py: $(top_srcdir)/patchlevel.h + -e 's|@SBINDIR@|$(SERVER_BINDIR)|g' \ + -e 's|@LIBDIR@|$(KRB5_LIBDIR)|g' \ + -e 's|@LOCALSTATEDIR@|$(localstatedir)|g' \ ++ -e 's|@RUNSTATEDIR@|$(runstatedir)|g' \ + -e 's|@SYSCONFDIR@|$(sysconfdir)|g' \ + -e 's|@CCNAME@|$(DEFCCNAME)|g' \ + -e 's|@KTNAME@|$(DEFKTNAME)|g' \ +diff --git a/src/plugins/preauth/otp/otp_state.c b/src/plugins/preauth/otp/otp_state.c +index a4d7e3b..4643dff 100644 +--- a/src/plugins/preauth/otp/otp_state.c ++++ b/src/plugins/preauth/otp/otp_state.c +@@ -40,7 +40,7 @@ + #endif + + #define DEFAULT_TYPE_NAME "DEFAULT" +-#define DEFAULT_SOCKET_FMT KDC_DIR "/%s.socket" ++#define DEFAULT_SOCKET_FMT KDC_RUN_DIR "/%s.socket" + #define DEFAULT_TIMEOUT 5 + #define DEFAULT_RETRIES 3 + #define MAX_SECRET_LEN 1024 diff --git a/SOURCES/krb5-1.12-pwdch-fast.patch b/SOURCES/krb5-1.12-pwdch-fast.patch new file mode 100644 index 0000000..4751c9f --- /dev/null +++ b/SOURCES/krb5-1.12-pwdch-fast.patch @@ -0,0 +1,177 @@ +From 0c00989f5bdaf9adf6d243455fbc7056bd0013f5 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Fri, 28 Feb 2014 14:49:35 -0500 +Subject: [PATCH] Use preauth options when changing password + +If we try to change the password in rb5_get_init_creds_password, we +must use all application-specified gic options which affect +preauthentication when getting the kadmin/changepw ticket. Create a +helper function make_chpw_options which copies the application's +options, unsets the options we don't want, and sets options +appropriate for a temporary ticket. + +ticket: 7868 + +npmccallum: + * include tests from 06817686bfdef99523f300464bcbb0c8b037a27d + * backport from 1.12 to 1.11 +--- + src/lib/krb5/krb/gic_pwd.c | 63 +++++++++++++++++++++++++++++++++++++--------- + src/tests/Makefile.in | 1 + + src/tests/t_changepw.py | 37 +++++++++++++++++++++++++++ + 3 files changed, 89 insertions(+), 12 deletions(-) + create mode 100644 src/tests/t_changepw.py + +diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c +index 9fdb8934a0765395ebc2b875d31b393d954bcbb4..c6c3161ca777698f0a7cbb789df34e51fa85b12c 100644 +--- a/src/lib/krb5/krb/gic_pwd.c ++++ b/src/lib/krb5/krb/gic_pwd.c +@@ -241,6 +241,54 @@ warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options, + (*prompter)(context, data, 0, banner, 0, 0); + } + ++/* ++ * Create a temporary options structure for getting a kadmin/changepw ticket, ++ * based on the appplication-specified options. Propagate all application ++ * options which affect preauthentication, but not options which affect the ++ * resulting ticket or how it is stored. Set lifetime and flags appropriate ++ * for a ticket which we will use immediately and then discard. ++ * ++ * storage1 and storage2 will be used to hold the temporary options. The ++ * caller must not free the result, as it will contain aliases into the ++ * application options. ++ */ ++static krb5_get_init_creds_opt * ++make_chpw_options(krb5_get_init_creds_opt *in, krb5_gic_opt_ext *storage1, ++ krb5_gic_opt_private *storage2) ++{ ++ krb5_gic_opt_ext *in_ext; ++ krb5_get_init_creds_opt *opt; ++ ++ /* Copy the application's options to storage. */ ++ if (in == NULL) { ++ storage1->flags = 0; ++ } else if (krb5_gic_opt_is_extended(in)) { ++ in_ext = (krb5_gic_opt_ext *)in; ++ *storage1 = *in_ext; ++ *storage2 = *in_ext->opt_private; ++ storage1->opt_private = storage2; ++ } else { ++ *(krb5_get_init_creds_opt *)storage1 = *in; ++ } ++ ++ /* Get a non-forwardable, non-proxiable, short-lifetime ticket. */ ++ opt = (krb5_get_init_creds_opt *)storage1; ++ krb5_get_init_creds_opt_set_tkt_life(opt, 5 * 60); ++ krb5_get_init_creds_opt_set_renew_life(opt, 0); ++ krb5_get_init_creds_opt_set_forwardable(opt, 0); ++ krb5_get_init_creds_opt_set_proxiable(opt, 0); ++ ++ /* Unset options which should only apply to the actual ticket. */ ++ opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST; ++ opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ANONYMOUS; ++ ++ /* The output ccache should only be used for the actual ticket. */ ++ if (krb5_gic_opt_is_extended(opt)) ++ storage2->out_ccache = NULL; ++ ++ return opt; ++} ++ + krb5_error_code KRB5_CALLCONV + krb5_get_init_creds_password(krb5_context context, + krb5_creds *creds, +@@ -258,6 +306,8 @@ krb5_get_init_creds_password(krb5_context context, + int tries; + krb5_creds chpw_creds; + krb5_get_init_creds_opt *chpw_opts = NULL; ++ krb5_gic_opt_ext storage1; ++ krb5_gic_opt_private storage2; + struct gak_password gakpw; + krb5_data pw0, pw1; + char banner[1024], pw0array[1024], pw1array[1024]; +@@ -347,16 +397,7 @@ krb5_get_init_creds_password(krb5_context context, + /* ok, we have an expired password. Give the user a few chances + to change it */ + +- /* use a minimal set of options */ +- +- ret = krb5_get_init_creds_opt_alloc(context, &chpw_opts); +- if (ret) +- goto cleanup; +- krb5_get_init_creds_opt_set_tkt_life(chpw_opts, 5*60); +- krb5_get_init_creds_opt_set_renew_life(chpw_opts, 0); +- krb5_get_init_creds_opt_set_forwardable(chpw_opts, 0); +- krb5_get_init_creds_opt_set_proxiable(chpw_opts, 0); +- ++ chpw_opts = make_chpw_options(options, &storage1, &storage2); + if ((ret = krb5int_get_init_creds(context, &chpw_creds, client, + prompter, data, + start_time, "kadmin/changepw", chpw_opts, +@@ -473,8 +514,6 @@ cleanup: + warn_pw_expiry(context, options, prompter, data, in_tkt_service, + as_reply); + +- if (chpw_opts) +- krb5_get_init_creds_opt_free(context, chpw_opts); + zapfree(gakpw.storage.data, gakpw.storage.length); + memset(pw0array, 0, sizeof(pw0array)); + memset(pw1array, 0, sizeof(pw1array)); +diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in +index 9f76e9a7fad23e03b37e067d58c75fd7eaa8e673..74a6bdcff95618b6c9c77d5b50a243790af557e3 100644 +--- a/src/tests/Makefile.in ++++ b/src/tests/Makefile.in +@@ -71,6 +71,7 @@ check-pytests:: hist t_init_creds + $(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_anonpkinit.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_lockout.py $(PYTESTFLAGS) ++ $(RUNPYTEST) $(srcdir)/t_changepw.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_kadm5_hook.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_kdb_locking.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_keyrollover.py $(PYTESTFLAGS) +diff --git a/src/tests/t_changepw.py b/src/tests/t_changepw.py +new file mode 100644 +index 0000000000000000000000000000000000000000..0b9832668e618b3db8d88cf388ec918898bb4df3 +--- /dev/null ++++ b/src/tests/t_changepw.py +@@ -0,0 +1,37 @@ ++#!/usr/bin/python ++from k5test import * ++ ++# This file is intended to cover any password-changing mechanism. For ++# now it only contains a regression test for #7868. ++ ++realm = K5Realm(create_host=False, get_creds=False, start_kadmind=True) ++ ++# Mark a principal as expired and change its password through kinit. ++realm.run_kadminl('modprinc -pwexpire "1 day ago" user') ++pwinput = password('user') + '\nabcd\nabcd\n' ++realm.run([kinit, realm.user_princ], input=pwinput) ++ ++# Do the same thing with FAST, with tracing turned on. ++realm.run_kadminl('modprinc -pwexpire "1 day ago" user') ++pwinput = 'abcd\nefgh\nefgh\n' ++tracefile = os.path.join(realm.testdir, 'trace') ++realm.run(['env', 'KRB5_TRACE=' + tracefile, kinit, '-T', realm.ccache, ++ realm.user_princ], input=pwinput) ++ ++# Read the trace and check that FAST was used when getting the ++# kadmin/changepw ticket. ++f = open(tracefile, 'r') ++trace = f.read() ++f.close() ++getting_changepw = fast_used_for_changepw = False ++for line in trace.splitlines(): ++ if 'Getting initial credentials for user@' in line: ++ getting_changepw_ticket = False ++ if 'Setting initial creds service to kadmin/changepw' in line: ++ getting_changepw_ticket = True ++ if getting_changepw_ticket and 'Using FAST' in line: ++ fast_used_for_changepw = True ++if not fast_used_for_changepw: ++ fail('FAST was not used to get kadmin/changepw ticket') ++ ++success('Password change tests') +-- +1.8.5.3 + diff --git a/SOURCES/krb5-CVE-2013-1417.patch b/SOURCES/krb5-CVE-2013-1417.patch new file mode 100644 index 0000000..9b1d0b6 --- /dev/null +++ b/SOURCES/krb5-CVE-2013-1417.patch @@ -0,0 +1,68 @@ +commit 4c023ba43c16396f0d199e2df1cfa59b88b62acc +Author: Tom Yu +Date: Fri Jun 21 17:58:25 2013 -0400 + + KDC null deref due to referrals [CVE-2013-1417] + + An authenticated remote client can cause a KDC to crash by making a + valid TGS-REQ to a KDC serving a realm with a single-component name. + The process_tgs_req() function dereferences a null pointer because an + unusual failure condition causes a helper function to return success. + + While attempting to provide cross-realm referrals for host-based + service principals, the find_referral_tgs() function could return a + TGS principal for a zero-length realm name (indicating that the + hostname in the service principal has no known realm associated with + it). + + Subsequently, the find_alternate_tgs() function would attempt to + construct a path to this empty-string realm, and return success along + with a null pointer in its output parameter. This happens because + krb5_walk_realm_tree() returns a list of length one when it attempts + to construct a transit path between a single-component realm and the + empty-string realm. This list causes a loop in find_alternate_tgs() + to iterate over zero elements, resulting in the unexpected output of a + null pointer, which process_tgs_req() proceeds to dereference because + there is no error condition. + + Add an error condition to find_referral_tgs() when + krb5_get_host_realm() returns an empty realm name. Also add an error + condition to find_alternate_tgs() to handle the length-one output from + krb5_walk_realm_tree(). + + The vulnerable configuration is not likely to arise in practice. + (Realm names that have a single component are likely to be test + realms.) Releases prior to krb5-1.11 are not vulnerable. + + Thanks to Sol Jerome for reporting this problem. + + CVSSv2: AV:N/AC:M/Au:S/C:N/I:N/A:P/E:H/RL:O/RC:C + + (cherry picked from commit 3c7f1c21ffaaf6c90f1045f0f5440303c766acc0) + + ticket: 7668 + version_fixed: 1.11.4 + status: resolved + +diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c +index d41bc5d..745a48e 100644 +--- a/src/kdc/do_tgs_req.c ++++ b/src/kdc/do_tgs_req.c +@@ -1057,6 +1057,8 @@ find_alternate_tgs(kdc_realm_t *kdc_active_realm, krb5_principal princ, + goto cleanup; + } + cleanup: ++ if (retval == 0 && server_ptr == NULL) ++ retval = KRB5_KDB_NOENTRY; + if (retval != 0) + *status = "UNKNOWN_SERVER"; + +@@ -1149,7 +1151,7 @@ find_referral_tgs(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request, + goto cleanup; + } + /* Don't return a referral to the empty realm or the service realm. */ +- if (realms == NULL || realms[0] == '\0' || ++ if (realms == NULL || realms[0] == NULL || *realms[0] == '\0' || + data_eq_string(srealm, realms[0])) { + retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto cleanup; diff --git a/SOURCES/krb5-keyring-strtol.patch b/SOURCES/krb5-keyring-strtol.patch new file mode 100644 index 0000000..790bbc1 --- /dev/null +++ b/SOURCES/krb5-keyring-strtol.patch @@ -0,0 +1,41 @@ +commit 5ac159e220297a8f62dd5edcec6f9b988b0627ea +Author: Nalin Dahyabhai +Date: Mon Nov 11 13:10:08 2013 -0500 + + Catch more strtol() failures when using KEYRINGs + + When parsing what should be a UID while resolving a KEYRING ccache + name, don't just depend on strtol() to set errno when the residual + that we pass to it can't be parsed as a number. In addition to + checking errno, pass in and check the value of an "endptr". + + [ghudson@mit.edu: simplified slightly] + + ticket: 7764 (new) + target_version: 1.12 + tags: pullup + +diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c +index 795ccd6..a07a0dc 100644 +--- a/src/lib/krb5/ccache/cc_keyring.c ++++ b/src/lib/krb5/ccache/cc_keyring.c +@@ -593,7 +593,7 @@ get_collection(const char *anchor_name, const char *collection_name, + { + krb5_error_code ret; + key_serial_t persistent_id, anchor_id, possess_id = 0; +- char *ckname; ++ char *ckname, *cnend; + long uidnum; + + *collection_id_out = 0; +@@ -607,8 +607,8 @@ get_collection(const char *anchor_name, const char *collection_name, + */ + if (*collection_name != '\0') { + errno = 0; +- uidnum = strtol(collection_name, NULL, 10); +- if (errno) ++ uidnum = strtol(collection_name, &cnend, 10); ++ if (errno || *cnend != '\0') + return KRB5_KCC_INVALID_UID; + } else { + uidnum = geteuid(); diff --git a/SOURCES/krb5-krb5kdc.conf b/SOURCES/krb5-krb5kdc.conf new file mode 100644 index 0000000..eadeb51 --- /dev/null +++ b/SOURCES/krb5-krb5kdc.conf @@ -0,0 +1 @@ +d /var/run/krb5kdc 0755 root root diff --git a/SOURCES/krb5-master-empty-credstore.patch b/SOURCES/krb5-master-empty-credstore.patch new file mode 100644 index 0000000..b6cd7ef --- /dev/null +++ b/SOURCES/krb5-master-empty-credstore.patch @@ -0,0 +1,43 @@ +commit 970304b558a360e08d8421ef92245d2df0ac5e49 +Author: Greg Hudson +Date: Thu Jan 16 11:49:04 2014 -0500 + + Allow empty store in gss_acquire_cred_from + + There is no reason to deny a zero-length cred store, so don't check + for it in val_acq_cred_args or val_add_cred_args. + + ticket: 7836 (new) + target_version: 1.12.2 + tags: pullup + +diff --git a/src/lib/gssapi/mechglue/g_acquire_cred.c b/src/lib/gssapi/mechglue/g_acquire_cred.c +index 03b67e3..b9a3142 100644 +--- a/src/lib/gssapi/mechglue/g_acquire_cred.c ++++ b/src/lib/gssapi/mechglue/g_acquire_cred.c +@@ -80,12 +80,6 @@ val_acq_cred_args( + return GSS_S_FAILURE; + } + +- if (cred_store != NULL && cred_store->count == 0) { +- *minor_status = EINVAL; +- map_errcode(minor_status); +- return GSS_S_FAILURE; +- } +- + return (GSS_S_COMPLETE); + } + +@@ -302,12 +296,6 @@ val_add_cred_args( + return GSS_S_FAILURE; + } + +- if (cred_store != NULL && cred_store->count == 0) { +- *minor_status = EINVAL; +- map_errcode(minor_status); +- return GSS_S_FAILURE; +- } +- + return (GSS_S_COMPLETE); + } + diff --git a/SOURCES/krb5-master-gss_oid_leak.patch b/SOURCES/krb5-master-gss_oid_leak.patch new file mode 100644 index 0000000..9613823 --- /dev/null +++ b/SOURCES/krb5-master-gss_oid_leak.patch @@ -0,0 +1,28 @@ +commit 1cda48a7ed4069cfc052f974ec3d76a9137c8c5a +Author: Simo Sorce +Date: Fri Dec 13 12:00:41 2013 -0500 + + Fix memory leak in SPNEGO initiator + + If we eliminate a mechanism from the initiator list because + gss_init_sec_context fails, free the memory for that mech OID before + removing it from the list. + + [ghudson@mit.edu: clarified commit message] + + ticket: 7803 (new) + target_version: 1.12.1 + tags: pullup + +diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c +index 818a1b4..06cfab0 100644 +--- a/src/lib/gssapi/spnego/spnego_mech.c ++++ b/src/lib/gssapi/spnego/spnego_mech.c +@@ -890,6 +890,7 @@ init_ctx_call_init(OM_uint32 *minor_status, + * can do this with recursion. If all mechanisms produce errors, the + * caller should get the error from the first mech in the list. + */ ++ gssalloc_free(sc->mech_set->elements->elements); + memmove(sc->mech_set->elements, sc->mech_set->elements + 1, + --sc->mech_set->count * sizeof(*sc->mech_set->elements)); + if (sc->mech_set->count == 0) diff --git a/SOURCES/krb5-master-ignore-empty-unnecessary-final-token.patch b/SOURCES/krb5-master-ignore-empty-unnecessary-final-token.patch new file mode 100644 index 0000000..3ebb888 --- /dev/null +++ b/SOURCES/krb5-master-ignore-empty-unnecessary-final-token.patch @@ -0,0 +1,37 @@ +commit 37af638b742dbd642eb70092e4f7781c3f69d86d +Author: Greg Hudson +Date: Tue Dec 10 12:04:18 2013 -0500 + + Fix SPNEGO one-hop interop against old IIS + + IIS 6.0 and similar return a zero length reponse buffer in the last + SPNEGO packet when context initiation is performed without mutual + authentication. In this case the underlying Kerberos mechanism has + already completed successfully on the first invocation, and SPNEGO + does not expect a mech response token in the answer. If we get an + empty mech response token when the mech is complete during + negotiation, ignore it. + + [ghudson@mit.edu: small code style and commit message changes] + + ticket: 7797 (new) + target_version: 1.12.1 + tags: pullup + +diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c +index 3937662..d82934b 100644 +--- a/src/lib/gssapi/spnego/spnego_mech.c ++++ b/src/lib/gssapi/spnego/spnego_mech.c +@@ -760,6 +760,12 @@ init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, + map_errcode(minor_status); + ret = GSS_S_DEFECTIVE_TOKEN; + } ++ } else if ((*responseToken)->length == 0 && sc->mech_complete) { ++ /* Handle old IIS servers returning empty token instead of ++ * null tokens in the non-mutual auth case. */ ++ *negState = ACCEPT_COMPLETE; ++ *tokflag = NO_TOKEN_SEND; ++ ret = GSS_S_COMPLETE; + } else if (sc->mech_complete) { + /* Reject spurious mech token. */ + ret = GSS_S_DEFECTIVE_TOKEN; diff --git a/SOURCES/krb5-master-keyring-expiration.patch b/SOURCES/krb5-master-keyring-expiration.patch new file mode 100644 index 0000000..2c58cb0 --- /dev/null +++ b/SOURCES/krb5-master-keyring-expiration.patch @@ -0,0 +1,127 @@ +commit 29e60c5b7ac0980606971afc6fd6028bcf0c7f0f +Author: Simo Sorce +Date: Fri Nov 15 16:36:05 2013 -0500 + + Set expiration time on keys and keyrings + + By setting the timeout based on the credetial's timeout we let the + system automatically cleanup expired credentials. + + [ghudson@mit.edu: simplified code slightly] + + ticket: 7769 (new) + target_version: 1.12 + tags: pullup + +diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c +index 2192fa5..1a0f1df 100644 +--- a/src/lib/krb5/ccache/cc_keyring.c ++++ b/src/lib/krb5/ccache/cc_keyring.c +@@ -818,21 +818,68 @@ cleanup: + + static krb5_error_code + add_cred_key(const char *name, const void *payload, size_t plen, +- key_serial_t cache_id, krb5_boolean legacy_type) ++ key_serial_t cache_id, krb5_boolean legacy_type, ++ key_serial_t *key_out) + { + key_serial_t key; + ++ *key_out = -1; + if (!legacy_type) { + /* Try the preferred cred key type; fall back if no kernel support. */ + key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id); +- if (key != -1) ++ if (key != -1) { ++ *key_out = key; + return 0; +- else if (errno != EINVAL && errno != ENODEV) ++ } else if (errno != EINVAL && errno != ENODEV) { + return errno; ++ } + } + /* Use the user key type. */ + key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id); +- return (key == -1) ? errno : 0; ++ if (key == -1) ++ return errno; ++ *key_out = key; ++ return 0; ++} ++ ++static void ++update_keyring_expiration(krb5_context context, krb5_ccache id) ++{ ++ krb5_krcc_data *d = (krb5_krcc_data *)id->data; ++ krb5_cc_cursor cursor; ++ krb5_creds creds; ++ krb5_timestamp now, endtime = 0; ++ unsigned int timeout; ++ ++ /* ++ * We have no way to know what is the actual timeout set on the keyring. ++ * We also cannot keep track of it in a local variable as another process ++ * can always modify the keyring independently, so just always enumerate ++ * all keys and find out the highest endtime time. ++ */ ++ ++ /* Find the maximum endtime of all creds in the cache. */ ++ if (krb5_krcc_start_seq_get(context, id, &cursor) != 0) ++ return; ++ for (;;) { ++ if (krb5_krcc_next_cred(context, id, &cursor, &creds) != 0) ++ break; ++ if (creds.times.endtime > endtime) ++ endtime = creds.times.endtime; ++ krb5_free_cred_contents(context, &creds); ++ } ++ (void)krb5_krcc_end_seq_get(context, id, &cursor); ++ ++ if (endtime == 0) /* No creds with end times */ ++ return; ++ ++ if (krb5_timeofday(context, &now) != 0) ++ return; ++ ++ /* Setting the timeout to zero would reset the timeout, so we set it to one ++ * second instead if creds are already expired. */ ++ timeout = (endtime > now) ? endtime - now : 1; ++ (void)keyctl_set_timeout(d->cache_id, timeout); + } + + /* +@@ -1497,6 +1544,8 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds) + char *payload = NULL; + unsigned int payloadlen; + char *keyname = NULL; ++ key_serial_t cred_key; ++ krb5_timestamp now; + + DEBUG_PRINT(("krb5_krcc_store: entered\n")); + +@@ -1523,12 +1572,24 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds) + DEBUG_PRINT(("krb5_krcc_store: adding new key '%s' to keyring %d\n", + keyname, d->cache_id)); + kret = add_cred_key(keyname, payload, payloadlen, d->cache_id, +- d->is_legacy_type); ++ d->is_legacy_type, &cred_key); + if (kret) + goto errout; + + krb5_krcc_update_change_time(d); + ++ /* Set appropriate timeouts on cache keys. */ ++ kret = krb5_timeofday(context, &now); ++ if (kret) ++ goto errout; ++ ++ if (creds->times.endtime > now) ++ (void)keyctl_set_timeout(cred_key, creds->times.endtime - now); ++ ++ update_keyring_expiration(context, id); ++ ++ kret = KRB5_OK; ++ + errout: + if (keyname) + krb5_free_unparsed_name(context, keyname); diff --git a/SOURCES/krb5-master-keyring-kdcsync.patch b/SOURCES/krb5-master-keyring-kdcsync.patch new file mode 100644 index 0000000..3079bf2 --- /dev/null +++ b/SOURCES/krb5-master-keyring-kdcsync.patch @@ -0,0 +1,108 @@ +commit e99c688913a7761c6adea9488ea9355f43539883 +Author: Greg Hudson +Date: Thu Jan 16 17:48:54 2014 -0500 + + Get time offsets for all keyring ccaches + + Move the time offset lookup from krb5_krcc_resolve to make_cache, so + that we fetch time offsets for caches created by + krb5_krcc_ptcursor_next. + + ticket: 7820 + target_version: 1.12.2 + tags: pullup + +diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c +index a0c8035..27bad9d 100644 +--- a/src/lib/krb5/ccache/cc_keyring.c ++++ b/src/lib/krb5/ccache/cc_keyring.c +@@ -1077,11 +1077,13 @@ krb5_krcc_destroy(krb5_context context, krb5_ccache id) + + /* Create a cache handle for a cache ID. */ + static krb5_error_code +-make_cache(key_serial_t collection_id, key_serial_t cache_id, +- const char *anchor_name, const char *collection_name, +- const char *subsidiary_name, krb5_ccache *cache_out) ++make_cache(krb5_context context, key_serial_t collection_id, ++ key_serial_t cache_id, const char *anchor_name, ++ const char *collection_name, const char *subsidiary_name, ++ krb5_ccache *cache_out) + { + krb5_error_code ret; ++ krb5_os_context os_ctx = &context->os_context; + krb5_ccache ccache = NULL; + krb5_krcc_data *d; + key_serial_t pkey = 0; +@@ -1108,6 +1110,18 @@ make_cache(key_serial_t collection_id, key_serial_t cache_id, + ccache->data = d; + ccache->magic = KV5M_CCACHE; + *cache_out = ccache; ++ ++ /* Lookup time offsets if necessary. */ ++ if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) && ++ !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) { ++ if (krb5_krcc_get_time_offsets(context, ccache, ++ &os_ctx->time_offset, ++ &os_ctx->usec_offset) == 0) { ++ os_ctx->os_flags &= ~KRB5_OS_TOFFSET_TIME; ++ os_ctx->os_flags |= KRB5_OS_TOFFSET_VALID; ++ } ++ } ++ + return 0; + } + +@@ -1134,7 +1148,6 @@ make_cache(key_serial_t collection_id, key_serial_t cache_id, + static krb5_error_code KRB5_CALLCONV + krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual) + { +- krb5_os_context os_ctx = &context->os_context; + krb5_error_code ret; + key_serial_t collection_id, cache_id; + char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL; +@@ -1161,22 +1174,11 @@ krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual) + if (cache_id < 0) + cache_id = 0; + +- ret = make_cache(collection_id, cache_id, anchor_name, collection_name, +- subsidiary_name, id); ++ ret = make_cache(context, collection_id, cache_id, anchor_name, ++ collection_name, subsidiary_name, id); + if (ret) + goto cleanup; + +- /* Lookup time offsets if necessary. */ +- if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) && +- !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) { +- if (krb5_krcc_get_time_offsets(context, *id, +- &os_ctx->time_offset, +- &os_ctx->usec_offset) == 0) { +- os_ctx->os_flags &= ~KRB5_OS_TOFFSET_TIME; +- os_ctx->os_flags |= KRB5_OS_TOFFSET_VALID; +- } +- } +- + cleanup: + free(anchor_name); + free(collection_name); +@@ -1928,8 +1930,9 @@ krb5_krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor, + cache_id = keyctl_search(data->collection_id, KRCC_KEY_TYPE_KEYRING, + first_name, 0); + if (cache_id != -1) { +- return make_cache(data->collection_id, cache_id, data->anchor_name, +- data->collection_name, first_name, cache_out); ++ return make_cache(context, data->collection_id, cache_id, ++ data->anchor_name, data->collection_name, ++ first_name, cache_out); + } + } + +@@ -1967,7 +1970,7 @@ krb5_krcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor, + + /* We found a valid key */ + data->next_key++; +- ret = make_cache(data->collection_id, key, data->anchor_name, ++ ret = make_cache(context, data->collection_id, key, data->anchor_name, + data->collection_name, subsidiary_name, cache_out); + free(description); + return ret; diff --git a/SOURCES/krb5-master-keyring-offsets.patch b/SOURCES/krb5-master-keyring-offsets.patch new file mode 100644 index 0000000..b54e84b --- /dev/null +++ b/SOURCES/krb5-master-keyring-offsets.patch @@ -0,0 +1,311 @@ +commit fb4817a32d0c369049e0868468dd2eb75487630d +Author: Simo Sorce +Date: Thu Nov 14 17:23:59 2013 -0500 + + Add support to store time offsets in cc_keyring + + The code follows the same model used for the memory ccache type. Time + offsets are stored in each credential cache in a special key just like + the principal name. Legacy session caches do not store timestamps as + legacy code would fail when iterating over the new offset key. + + [ghudson@mit.edu: minor formatting changes; note legacy session + exception in commit message] + + ticket: 7768 (new) + target_version: 1.12 + tags: pullup + +diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c +index a07a0dc..2192fa5 100644 +--- a/src/lib/krb5/ccache/cc_keyring.c ++++ b/src/lib/krb5/ccache/cc_keyring.c +@@ -189,6 +189,11 @@ debug_print(char *fmt, ...) + #define KRCC_PERSISTENT_KEYRING_NAME "_krb" + + /* ++ * Name of the key holding time offsets for the individual cache ++ */ ++#define KRCC_TIME_OFFSETS "__krb5_time_offsets__" ++ ++/* + * Keyring name prefix and length of random name part + */ + #define KRCC_NAME_PREFIX "krb_ccache_" +@@ -217,6 +222,7 @@ typedef struct _krb5_krcc_cursor + int numkeys; + int currkey; + key_serial_t princ_id; ++ key_serial_t offsets_id; + key_serial_t *keys; + } *krb5_krcc_cursor; + +@@ -340,6 +346,12 @@ static krb5_error_code krb5_krcc_save_principal + + static krb5_error_code krb5_krcc_retrieve_principal + (krb5_context context, krb5_ccache id, krb5_principal * princ); ++static krb5_error_code krb5_krcc_save_time_offsets ++(krb5_context context, krb5_ccache id, krb5_int32 time_offset, ++ krb5_int32 usec_offset); ++static krb5_error_code krb5_krcc_get_time_offsets ++(krb5_context context, krb5_ccache id, krb5_int32 *time_offset, ++ krb5_int32 *usec_offset); + + /* Routines to parse a key from a keyring into a cred structure */ + static krb5_error_code krb5_krcc_parse +@@ -410,6 +422,12 @@ krb5_krcc_parse_index(krb5_context context, krb5_int32 *version, + static krb5_error_code + krb5_krcc_unparse_index(krb5_context context, krb5_int32 version, + const char *primary, void **datapp, int *lenptr); ++static krb5_error_code ++krb5_krcc_parse_offsets(krb5_context context, krb5_int32 *time_offset, ++ krb5_int32 *usec_offset, void *payload, int psize); ++static krb5_error_code ++krb5_krcc_unparse_offsets(krb5_context context, krb5_int32 time_offset, ++ krb5_int32 usec_offset, void **datapp, int *lenptr); + + /* Note the following is a stub function for Linux */ + extern krb5_error_code krb5_change_cache(void); +@@ -835,6 +853,7 @@ krb5_krcc_initialize(krb5_context context, krb5_ccache id, + krb5_principal princ) + { + krb5_krcc_data *data = (krb5_krcc_data *)id->data; ++ krb5_os_context os_ctx = &context->os_context; + krb5_error_code kret; + const char *cache_name, *p; + +@@ -863,6 +882,15 @@ krb5_krcc_initialize(krb5_context context, krb5_ccache id, + (void)keyctl_link(data->cache_id, KEY_SPEC_SESSION_KEYRING); + + kret = krb5_krcc_save_principal(context, id, princ); ++ ++ /* Save time offset if it is valid and this is not a legacy cache. Legacy ++ * applications would fail to parse the new key in the cache keyring. */ ++ if (!is_legacy_cache_name(data->name) && ++ (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) { ++ kret = krb5_krcc_save_time_offsets(context, id, os_ctx->time_offset, ++ os_ctx->usec_offset); ++ } ++ + if (kret == KRB5_OK) + krb5_change_cache(); + +@@ -1039,6 +1067,7 @@ make_cache(key_serial_t collection_id, key_serial_t cache_id, + static krb5_error_code KRB5_CALLCONV + krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual) + { ++ krb5_os_context os_ctx = &context->os_context; + krb5_error_code ret; + key_serial_t collection_id, cache_id; + char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL; +@@ -1067,6 +1096,19 @@ krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual) + + ret = make_cache(collection_id, cache_id, anchor_name, collection_name, + subsidiary_name, id); ++ if (ret) ++ goto cleanup; ++ ++ /* Lookup time offsets if necessary. */ ++ if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) && ++ !(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) { ++ if (krb5_krcc_get_time_offsets(context, *id, ++ &os_ctx->time_offset, ++ &os_ctx->usec_offset) == 0) { ++ os_ctx->os_flags &= ~KRB5_OS_TOFFSET_TIME; ++ os_ctx->os_flags |= KRB5_OS_TOFFSET_VALID; ++ } ++ } + + cleanup: + free(anchor_name); +@@ -1122,6 +1164,8 @@ krb5_krcc_start_seq_get(krb5_context context, krb5_ccache id, + } + + krcursor->princ_id = d->princ_id; ++ krcursor->offsets_id = keyctl_search(d->cache_id, KRCC_KEY_TYPE_USER, ++ KRCC_TIME_OFFSETS, 0); + krcursor->numkeys = size / sizeof(key_serial_t); + krcursor->keys = keys; + +@@ -1174,8 +1218,10 @@ krb5_krcc_next_cred(krb5_context context, krb5_ccache id, + if (krcursor->currkey >= krcursor->numkeys) + return KRB5_CC_END; + +- /* If we're pointing at the entry with the principal, skip it */ +- if (krcursor->keys[krcursor->currkey] == krcursor->princ_id) { ++ /* If we're pointing at the entry with the principal, or at the key ++ * with the time offsets, skip it. */ ++ while (krcursor->keys[krcursor->currkey] == krcursor->princ_id || ++ krcursor->keys[krcursor->currkey] == krcursor->offsets_id) { + krcursor->currkey++; + /* Check if we have now reached the end */ + if (krcursor->currkey >= krcursor->numkeys) +@@ -1621,6 +1667,84 @@ errout: + return kret; + } + ++static krb5_error_code ++krb5_krcc_save_time_offsets(krb5_context context, krb5_ccache id, ++ krb5_int32 time_offset, krb5_int32 usec_offset) ++{ ++ krb5_krcc_data *d = (krb5_krcc_data *)id->data; ++ krb5_error_code kret; ++ key_serial_t newkey; ++ void *payload = NULL; ++ int psize; ++ ++ k5_cc_mutex_assert_locked(context, &d->lock); ++ ++ /* Prepare the payload. */ ++ kret = krb5_krcc_unparse_offsets(context, time_offset, usec_offset, ++ &payload, &psize); ++ CHECK_N_GO(kret, errout); ++ ++ /* Add new key into keyring. */ ++ newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload, psize, ++ d->cache_id); ++ if (newkey == -1) { ++ kret = errno; ++ DEBUG_PRINT(("Error adding time offsets key: %s\n", strerror(kret))); ++ } else { ++ kret = KRB5_OK; ++ krb5_krcc_update_change_time(d); ++ } ++ ++errout: ++ free(payload); ++ return kret; ++} ++ ++static krb5_error_code ++krb5_krcc_get_time_offsets(krb5_context context, krb5_ccache id, ++ krb5_int32 *time_offset, krb5_int32 *usec_offset) ++{ ++ krb5_krcc_data *d = (krb5_krcc_data *)id->data; ++ krb5_error_code kret; ++ key_serial_t key; ++ krb5_int32 t, u; ++ void *payload = NULL; ++ int psize; ++ ++ k5_cc_mutex_lock(context, &d->lock); ++ ++ if (!d->cache_id) { ++ kret = KRB5_FCC_NOFILE; ++ goto errout; ++ } ++ ++ key = keyctl_search(d->cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, 0); ++ if (key == -1) { ++ kret = ENOENT; ++ goto errout; ++ } ++ ++ psize = keyctl_read_alloc(key, &payload); ++ if (psize == -1) { ++ DEBUG_PRINT(("Reading time offsets key %d: %s\n", ++ key, strerror(errno))); ++ kret = KRB5_CC_IO; ++ goto errout; ++ } ++ ++ kret = krb5_krcc_parse_offsets(context, &t, &u, payload, psize); ++ if (kret) ++ goto errout; ++ ++ *time_offset = t; ++ *usec_offset = u; ++ ++errout: ++ free(payload); ++ k5_cc_mutex_unlock(context, &d->lock); ++ return kret; ++} ++ + struct krcc_ptcursor_data { + key_serial_t collection_id; + char *anchor_name; +@@ -2656,6 +2780,83 @@ errout: + return kret; + } + ++static krb5_error_code ++krb5_krcc_parse_offsets(krb5_context context, krb5_int32 *time_offset, ++ krb5_int32 *usec_offset, void *payload, int psize) ++{ ++ krb5_error_code kret; ++ krb5_krcc_bc bc; ++ ++ bc.bpp = payload; ++ bc.endp = bc.bpp + psize; ++ ++ kret = krb5_krcc_parse_int32(context, time_offset, &bc); ++ CHECK_OUT(kret); ++ ++ kret = krb5_krcc_parse_int32(context, usec_offset, &bc); ++ CHECK_OUT(kret); ++ ++ return KRB5_OK; ++} ++ ++static krb5_error_code ++krb5_krcc_unparse_offsets_internal(krb5_context context, ++ krb5_int32 time_offset, ++ krb5_int32 usec_offset, ++ krb5_krcc_bc *bc) ++{ ++ krb5_error_code kret; ++ ++ kret = krb5_krcc_unparse_int32(context, time_offset, bc); ++ CHECK_OUT(kret); ++ ++ kret = krb5_krcc_unparse_int32(context, usec_offset, bc); ++ CHECK_OUT(kret); ++ ++ return KRB5_OK; ++} ++ ++static krb5_error_code ++krb5_krcc_unparse_offsets(krb5_context context, krb5_int32 time_offset, ++ krb5_int32 usec_offset, void **datapp, int *lenptr) ++{ ++ krb5_error_code kret; ++ krb5_krcc_bc bc; ++ char *buf; ++ ++ if (!datapp || !lenptr) ++ return EINVAL; ++ ++ *datapp = NULL; ++ *lenptr = 0; ++ ++ /* Do a dry run first to calculate the size. */ ++ bc.bpp = bc.endp = NULL; ++ bc.size = 0; ++ kret = krb5_krcc_unparse_offsets_internal(context, time_offset, ++ usec_offset, &bc); ++ CHECK_OUT(kret); ++ ++ buf = malloc(bc.size); ++ if (buf == NULL) ++ return ENOMEM; ++ ++ bc.bpp = buf; ++ bc.endp = buf + bc.size; ++ kret = krb5_krcc_unparse_offsets_internal(context, time_offset, ++ usec_offset, &bc); ++ CHECK(kret); ++ ++ /* Success! */ ++ *datapp = buf; ++ *lenptr = bc.bpp - buf; ++ kret = KRB5_OK; ++ ++errout: ++ if (kret) ++ free(buf); ++ return kret; ++} + /* + * Utility routine: called by krb5_krcc_* functions to keep + * result of krb5_krcc_last_change_time up to date. diff --git a/SOURCES/krb5-master-keyringccops.patch b/SOURCES/krb5-master-keyringccops.patch new file mode 100644 index 0000000..e979314 --- /dev/null +++ b/SOURCES/krb5-master-keyringccops.patch @@ -0,0 +1,63 @@ +commit bfdc0955657ba83940c63d1d9771b09edc0cb453 +Author: Nalin Dahyabhai +Date: Thu Dec 5 13:54:09 2013 -0500 + + Flag no-such-keyring errors in get/set-flags ccops + + When attempting to use a keyring cache that doesn't exist, return + KRB5_FCC_NOFILE errors during ccache get/set flags ops, and set an error + message when we fail to read a principal name, bringing us more in line + with the behavior we already have when using file-based caches. + +diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c +index 1a0f1df..421b228 100644 +--- a/src/lib/krb5/ccache/cc_keyring.c ++++ b/src/lib/krb5/ccache/cc_keyring.c +@@ -1521,16 +1521,34 @@ krb5_krcc_remove_cred(krb5_context context, krb5_ccache cache, + static krb5_error_code KRB5_CALLCONV + krb5_krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags) + { ++ krb5_krcc_data *d = (krb5_krcc_data *) id->data; ++ + DEBUG_PRINT(("krb5_krcc_set_flags: entered\n")); + ++ k5_cc_mutex_lock(context, &d->lock); ++ if (!d->cache_id) { ++ k5_cc_mutex_unlock(context, &d->lock); ++ return KRB5_FCC_NOFILE; ++ } ++ k5_cc_mutex_unlock(context, &d->lock); ++ + return KRB5_OK; + } + + static krb5_error_code KRB5_CALLCONV + krb5_krcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags * flags) + { ++ krb5_krcc_data *d = (krb5_krcc_data *) id->data; ++ + DEBUG_PRINT(("krb5_krcc_get_flags: entered\n")); + ++ k5_cc_mutex_lock(context, &d->lock); ++ if (!d->cache_id) { ++ k5_cc_mutex_unlock(context, &d->lock); ++ return KRB5_FCC_NOFILE; ++ } ++ k5_cc_mutex_unlock(context, &d->lock); ++ + *flags = 0; + return KRB5_OK; + } +@@ -1707,6 +1725,12 @@ krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id, + if (!d->cache_id || !d->princ_id) { + princ = 0L; + kret = KRB5_FCC_NOFILE; ++ if (d->name) { ++ krb5_set_error_message(context, kret, ++ _("Credentials cache keyring '%s' " ++ "not found"), ++ d->name); ++ } + goto errout; + } + diff --git a/SOURCES/krb5-master-keytab_close.patch b/SOURCES/krb5-master-keytab_close.patch new file mode 100644 index 0000000..d020ae6 --- /dev/null +++ b/SOURCES/krb5-master-keytab_close.patch @@ -0,0 +1,39 @@ +commit decccbcb5075f8fbc28a535a9b337afc84a15dee +Author: Greg Hudson +Date: Mon Dec 16 15:37:56 2013 -0500 + + Fix GSS krb5 acceptor acquire_cred error handling + + When acquiring acceptor creds with a specified name, if we fail to + open a replay cache, we leak the keytab handle. If there is no + specified name and we discover that there is no content in the keytab, + we leak the keytab handle and return the wrong major code. Memory + leak reported by Andrea Campi. + + ticket: 7805 + target_version: 1.12.1 + tags: pullup + +diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c +index 0efcad4..9547207 100644 +--- a/src/lib/gssapi/krb5/acquire_cred.c ++++ b/src/lib/gssapi/krb5/acquire_cred.c +@@ -225,6 +225,7 @@ acquire_accept_cred(krb5_context context, + code = krb5_get_server_rcache(context, &cred->name->princ->data[0], + &cred->rcache); + if (code) { ++ krb5_kt_close(context, kt); + *minor_status = code; + return GSS_S_FAILURE; + } +@@ -232,8 +233,9 @@ acquire_accept_cred(krb5_context context, + /* Make sure we have a keytab with keys in it. */ + code = krb5_kt_have_content(context, kt); + if (code) { ++ krb5_kt_close(context, kt); + *minor_status = code; +- return GSS_S_FAILURE; ++ return GSS_S_CRED_UNAVAIL; + } + } + diff --git a/SOURCES/krb5-master-no-malloc0.patch b/SOURCES/krb5-master-no-malloc0.patch new file mode 100644 index 0000000..e5b0e63 --- /dev/null +++ b/SOURCES/krb5-master-no-malloc0.patch @@ -0,0 +1,39 @@ +commit 13fd26e1863c79f616653f6a10a58c01f65fceff +Author: Greg Hudson +Date: Fri Dec 6 18:56:56 2013 -0500 + + Avoid malloc(0) in SPNEGO get_input_token + + If we read a zero-length token in spnego_mech.c's get_input_token(), + set the value pointer to NULL instead of calling malloc(0). + + ticket: 7794 (new) + +diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c +index 24c3440..3937662 100644 +--- a/src/lib/gssapi/spnego/spnego_mech.c ++++ b/src/lib/gssapi/spnego/spnego_mech.c +@@ -3140,14 +3140,17 @@ get_input_token(unsigned char **buff_in, unsigned int buff_length) + return (NULL); + + input_token->length = len; +- input_token->value = gssalloc_malloc(input_token->length); ++ if (input_token->length > 0) { ++ input_token->value = gssalloc_malloc(input_token->length); ++ if (input_token->value == NULL) { ++ free(input_token); ++ return (NULL); ++ } + +- if (input_token->value == NULL) { +- free(input_token); +- return (NULL); ++ memcpy(input_token->value, *buff_in, input_token->length); ++ } else { ++ input_token->value = NULL; + } +- +- (void) memcpy(input_token->value, *buff_in, input_token->length); + *buff_in += input_token->length; + return (input_token); + } diff --git a/SOURCES/krb5-master-rcache-acquirecred-cleanup.patch b/SOURCES/krb5-master-rcache-acquirecred-cleanup.patch new file mode 100644 index 0000000..72b97d3 --- /dev/null +++ b/SOURCES/krb5-master-rcache-acquirecred-cleanup.patch @@ -0,0 +1,105 @@ +commit ef8e19af863158e4c1abc15fc710aa8cfad38406 +Author: Greg Hudson +Date: Wed Jan 15 12:51:42 2014 -0500 + + Clean up GSS krb5 acquire_accept_cred + + Use a cleanup handler instead of releasing kt in multiple error + clauses. Wrap a long line and fix a comment with a missing word. + Rewrap the function arguments to use fewer lines. + +diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c +index 9547207..37cc6b5 100644 +--- a/src/lib/gssapi/krb5/acquire_cred.c ++++ b/src/lib/gssapi/krb5/acquire_cred.c +@@ -179,13 +179,13 @@ cleanup: + */ + + static OM_uint32 +-acquire_accept_cred(krb5_context context, +- OM_uint32 *minor_status, +- krb5_keytab req_keytab, +- krb5_gss_cred_id_rec *cred) ++acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, ++ krb5_keytab req_keytab, krb5_gss_cred_id_rec *cred) + { ++ OM_uint32 major; + krb5_error_code code; +- krb5_keytab kt; ++ krb5_keytab kt = NULL; ++ krb5_rcache rc = NULL; + + assert(cred->keytab == NULL); + +@@ -202,46 +202,54 @@ acquire_accept_cred(krb5_context context, + } + } + if (code) { +- *minor_status = code; +- return GSS_S_CRED_UNAVAIL; ++ major = GSS_S_CRED_UNAVAIL; ++ goto cleanup; + } + + if (cred->name != NULL) { +- /* Make sure we keys matching the desired name in the keytab. */ ++ /* Make sure we have keys matching the desired name in the keytab. */ + code = check_keytab(context, kt, cred->name); + if (code) { +- krb5_kt_close(context, kt); + if (code == KRB5_KT_NOTFOUND) { + char *errstr = (char *)krb5_get_error_message(context, code); +- krb5_set_error_message(context, KG_KEYTAB_NOMATCH, "%s", errstr); ++ krb5_set_error_message(context, KG_KEYTAB_NOMATCH, "%s", ++ errstr); + krb5_free_error_message(context, errstr); +- *minor_status = KG_KEYTAB_NOMATCH; +- } else +- *minor_status = code; +- return GSS_S_CRED_UNAVAIL; ++ code = KG_KEYTAB_NOMATCH; ++ } ++ major = GSS_S_CRED_UNAVAIL; ++ goto cleanup; + } + + /* Open the replay cache for this principal. */ + code = krb5_get_server_rcache(context, &cred->name->princ->data[0], +- &cred->rcache); ++ &rc); + if (code) { +- krb5_kt_close(context, kt); +- *minor_status = code; +- return GSS_S_FAILURE; ++ major = GSS_S_FAILURE; ++ goto cleanup; + } + } else { + /* Make sure we have a keytab with keys in it. */ + code = krb5_kt_have_content(context, kt); + if (code) { +- krb5_kt_close(context, kt); +- *minor_status = code; +- return GSS_S_CRED_UNAVAIL; ++ major = GSS_S_CRED_UNAVAIL; ++ goto cleanup; + } + } + + cred->keytab = kt; ++ kt = NULL; ++ cred->rcache = rc; ++ rc = NULL; ++ major = GSS_S_COMPLETE; + +- return GSS_S_COMPLETE; ++cleanup: ++ if (kt != NULL) ++ krb5_kt_close(context, kt); ++ if (rc != NULL) ++ krb5_rc_close(context, rc); ++ *minor_status = code; ++ return major; + } + #endif /* LEAN_CLIENT */ + diff --git a/SOURCES/krb5-master-rcache-acquirecred-leak.patch b/SOURCES/krb5-master-rcache-acquirecred-leak.patch new file mode 100644 index 0000000..0ae7b34 --- /dev/null +++ b/SOURCES/krb5-master-rcache-acquirecred-leak.patch @@ -0,0 +1,26 @@ +commit 9df0c4bdce6b88a01af51e4bbb9a365db00256d5 +Author: Greg Hudson +Date: Wed Jan 15 14:41:54 2014 -0500 + + Clean up rcache if GSS krb5 acquire_cred fails + + The error handler in acquire_cred_context didn't release the rcache, + which would cause it to leak if we failed after acquire_accept_cred. + + ticket: 7818 (new) + target_version: 1.12.2 + tags: pullup + +diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c +index 37cc6b5..f625c0c 100644 +--- a/src/lib/gssapi/krb5/acquire_cred.c ++++ b/src/lib/gssapi/krb5/acquire_cred.c +@@ -829,6 +829,8 @@ error_out: + if (cred->keytab) + krb5_kt_close(context, cred->keytab); + #endif /* LEAN_CLIENT */ ++ if (cred->rcache) ++ krb5_rc_close(context, cred->rcache); + if (cred->name) + kg_release_name(context, &cred->name); + k5_mutex_destroy(&cred->lock); diff --git a/SOURCES/krb5-master-rcache-internal-const.patch b/SOURCES/krb5-master-rcache-internal-const.patch new file mode 100644 index 0000000..5cb1108 --- /dev/null +++ b/SOURCES/krb5-master-rcache-internal-const.patch @@ -0,0 +1,46 @@ +commit 74ff6c4accb68bd1d6c652c55e66519720db9fc4 +Author: Greg Hudson +Date: Wed Jan 15 12:31:41 2014 -0500 + + Make rcache resolve functions take const char * + +diff --git a/src/include/k5-int.h b/src/include/k5-int.h +index bbc7fab..b4757a9 100644 +--- a/src/include/k5-int.h ++++ b/src/include/k5-int.h +@@ -1887,8 +1887,10 @@ krb5_error_code KRB5_CALLCONV + krb5int_cc_user_set_default_name(krb5_context context, const char *name); + + krb5_error_code krb5_rc_default(krb5_context, krb5_rcache *); +-krb5_error_code krb5_rc_resolve_type(krb5_context, krb5_rcache *,char *); +-krb5_error_code krb5_rc_resolve_full(krb5_context, krb5_rcache *,char *); ++krb5_error_code krb5_rc_resolve_type(krb5_context, krb5_rcache *, ++ const char *); ++krb5_error_code krb5_rc_resolve_full(krb5_context, krb5_rcache *, ++ const char *); + char *krb5_rc_get_type(krb5_context, krb5_rcache); + char *krb5_rc_default_type(krb5_context); + char *krb5_rc_default_name(krb5_context); +diff --git a/src/lib/krb5/rcache/rc_base.c b/src/lib/krb5/rcache/rc_base.c +index 2fc96c5..373ac30 100644 +--- a/src/lib/krb5/rcache/rc_base.c ++++ b/src/lib/krb5/rcache/rc_base.c +@@ -65,7 +65,8 @@ krb5_rc_register_type(krb5_context context, const krb5_rc_ops *ops) + } + + krb5_error_code +-krb5_rc_resolve_type(krb5_context context, krb5_rcache *idptr, char *type) ++krb5_rc_resolve_type(krb5_context context, krb5_rcache *idptr, ++ const char *type) + { + struct krb5_rc_typelist *t; + krb5_error_code err; +@@ -146,7 +147,7 @@ krb5_rc_default(krb5_context context, krb5_rcache *idptr) + + krb5_error_code + krb5_rc_resolve_full(krb5_context context, krb5_rcache *idptr, +- char *string_name) ++ const char *string_name) + { + char *type; + char *residual; diff --git a/SOURCES/krb5-master-spnego_error_messages.patch b/SOURCES/krb5-master-spnego_error_messages.patch new file mode 100644 index 0000000..0a14bd3 --- /dev/null +++ b/SOURCES/krb5-master-spnego_error_messages.patch @@ -0,0 +1,175 @@ +Test tweaked for 1.11.3. + +commit d160bc733a3dbeb6d84f4e175234ff18738d9f66 +Author: Simo Sorce +Date: Tue Dec 17 16:15:14 2013 -0500 + + Let SPNEGO display mechanism errors + + To avoid potential recursion we use a thread local variable that tells + us whether the ancestor was called via spnego_gss_display_name(). If + we detect recursion, we assume that we returned a com_err code like + ENOMEM and call error_message(); in the worst case that will result in + an "Unknown error" message. + + [ghudson@mit.edu: Edited comments and commit message; removed an + unneeded line of code.] + + ticket: 7045 + target_version: 1.12.1 + tags: pullup + +diff --git a/src/include/k5-thread.h b/src/include/k5-thread.h +index 1b7fa69..ab46ec3 100644 +--- a/src/include/k5-thread.h ++++ b/src/include/k5-thread.h +@@ -406,6 +406,7 @@ typedef enum { + K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, + K5_KEY_GSS_KRB5_CCACHE_NAME, + K5_KEY_GSS_KRB5_ERROR_MESSAGE, ++ K5_KEY_GSS_SPNEGO_STATUS, + #if defined(__MACH__) && defined(__APPLE__) + K5_KEY_IPC_CONNECTION_INFO, + #endif +diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c +index 06cfab0..7e4bf90 100644 +--- a/src/lib/gssapi/spnego/spnego_mech.c ++++ b/src/lib/gssapi/spnego/spnego_mech.c +@@ -85,8 +85,8 @@ extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int); + + + /* private routines for spnego_mechanism */ +-static spnego_token_t make_spnego_token(char *); +-static gss_buffer_desc make_err_msg(char *); ++static spnego_token_t make_spnego_token(const char *); ++static gss_buffer_desc make_err_msg(const char *); + static int g_token_size(gss_OID_const, unsigned int); + static int g_make_token_header(gss_OID_const, unsigned int, + unsigned char **, unsigned int); +@@ -316,6 +316,12 @@ int gss_krb5int_lib_init(void); + + int gss_spnegoint_lib_init(void) + { ++ int err; ++ ++ err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL); ++ if (err) ++ return err; ++ + #ifdef _GSS_STATIC_LINK + return gss_spnegomechglue_init(); + #else +@@ -1791,7 +1797,6 @@ cleanup: + } + #endif /* LEAN_CLIENT */ + +- + /*ARGSUSED*/ + OM_uint32 KRB5_CALLCONV + spnego_gss_display_status( +@@ -1802,6 +1807,9 @@ spnego_gss_display_status( + OM_uint32 *message_context, + gss_buffer_t status_string) + { ++ OM_uint32 maj = GSS_S_COMPLETE; ++ int ret; ++ + dsyslog("Entering display_status\n"); + + *message_context = 0; +@@ -1832,13 +1840,31 @@ spnego_gss_display_status( + "return a valid token")); + break; + default: +- status_string->length = 0; +- status_string->value = ""; ++ /* Not one of our minor codes; might be from a mech. Call back ++ * to gss_display_status, but first check for recursion. */ ++ if (k5_getspecific(K5_KEY_GSS_SPNEGO_STATUS) != NULL) { ++ /* Perhaps we returned a com_err code like ENOMEM. */ ++ const char *err = error_message(status_value); ++ *status_string = make_err_msg(err); ++ break; ++ } ++ /* Set a non-null pointer value; doesn't matter which one. */ ++ ret = k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, &ret); ++ if (ret != 0) { ++ *minor_status = ret; ++ maj = GSS_S_FAILURE; ++ break; ++ } ++ maj = gss_display_status(minor_status, status_value, ++ status_type, mech_type, ++ message_context, status_string); ++ /* This is unlikely to fail; not much we can do if it does. */ ++ (void)k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, NULL); + break; + } + + dsyslog("Leaving display_status\n"); +- return (GSS_S_COMPLETE); ++ return maj; + } + + +@@ -3550,13 +3576,13 @@ negotiate_mech(gss_OID_set supported, gss_OID_set received, + * these routines will be changes to return the error string. + */ + static spnego_token_t +-make_spnego_token(char *name) ++make_spnego_token(const char *name) + { + return (spnego_token_t)strdup(name); + } + + static gss_buffer_desc +-make_err_msg(char *name) ++make_err_msg(const char *name) + { + gss_buffer_desc buffer; + +commit 4faca53e3a8ee213d43da8998f6889e7bfd36248 +Author: Greg Hudson +Date: Wed Dec 18 16:03:16 2013 -0500 + + Test SPNEGO error message in t_s4u.py + + Now that #7045 is fixed, we can check for the correct error message + from t_s4u2proxy_krb5 with --spnego. + + ticket: 7045 + +diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py +index 67dc810..e4aa259 100644 +--- a/src/tests/gssapi/t_s4u.py ++++ b/src/tests/gssapi/t_s4u.py +@@ -30,12 +30,12 @@ if ('auth1: ' + realm.user_princ not in output or + 'NOT_ALLOWED_TO_DELEGATE' not in output): + fail('krb5 -> s4u2proxy') + +-# Again with SPNEGO. Bug #7045 prevents us from checking the error +-# message, but we can at least exercise the code. ++# Again with SPNEGO. + output = realm.run_as_server(['./t_s4u2proxy_krb5', '--spnego', usercache, + storagecache, '-', pservice1, pservice2], + expected_code=1) +-if ('auth1: ' + realm.user_princ not in output): ++if ('auth1: ' + realm.user_princ not in output or ++ 'NOT_ALLOWED_TO_DELEGATE' not in output): + fail('krb5 -> s4u2proxy (SPNEGO)') + + # Try krb5 -> S4U2Proxy without forwardable user creds. This should +@@ -66,10 +66,9 @@ if 'NOT_ALLOWED_TO_DELEGATE' not in output: + fail('s4u2self') + + # Again with SPNEGO. This uses SPNEGO for the initial authentication, +-# but still uses krb5 for S4U2Proxy (the delegated cred is returned as ++# but still uses krb5 for S4U2Proxy--the delegated cred is returned as + # a krb5 cred, not a SPNEGO cred, and t_s4u uses the delegated cred +-# directly rather than saving and reacquiring it) so bug #7045 does +-# not apply and we can verify the error message. ++# directly rather than saving and reacquiring it. + output = realm.run_as_server(['./t_s4u', '--spnego', puser, pservice2], + expected_code=1) + if 'NOT_ALLOWED_TO_DELEGATE' not in output: + fail('s4u2self') diff --git a/SOURCES/noport.c b/SOURCES/noport.c index c7a0c01..22088eb 100644 --- a/SOURCES/noport.c +++ b/SOURCES/noport.c @@ -87,21 +87,25 @@ sendto(int sockfd, const void *buf, size_t len, int flags, return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen); } - switch (dest_addr->sa_family) { - case AF_INET: - port = ntohs(((struct sockaddr_in *)dest_addr)->sin_port); - if (port_is_okay(port) != 0) { - return -1; - } - break; - case AF_INET6: - port = ntohs(((struct sockaddr_in6 *)dest_addr)->sin6_port); - if (port_is_okay(port) != 0) { - return -1; + if (dest_addr != NULL) { + switch (dest_addr->sa_family) { + case AF_INET: + port = ((struct sockaddr_in *)dest_addr)->sin_port; + port = ntohs(port); + if (port_is_okay(port) != 0) { + return -1; + } + break; + case AF_INET6: + port = ((struct sockaddr_in6 *)dest_addr)->sin6_port; + port = ntohs(port); + if (port_is_okay(port) != 0) { + return -1; + } + break; + default: + break; } - break; - default: - break; } return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen); } diff --git a/SOURCES/persistent_keyring.patch b/SOURCES/persistent_keyring.patch index 863774c..2e625b8 100644 --- a/SOURCES/persistent_keyring.patch +++ b/SOURCES/persistent_keyring.patch @@ -1,5 +1,4 @@ -Pared down from the git commits, with a local copy of k5memdup0() added in -to cc_keyring, and a wrapper 'run' in to k5test.py. +Pared down from the git commits, with a wrapper 'run' added to k5test.py. diff --git a/src/aclocal.m4 b/src/aclocal.m4 index 2c17e46..7be77c2 100644 @@ -238,7 +237,7 @@ index fd1bcec..795ccd6 100644 } krb5_krcc_bc; /* Global mutex */ -@@ -258,6 +311,29 @@ static krb5_error_code KRB5_CALLCONV krb5_krcc_lock +@@ -258,6 +311,18 @@ static krb5_error_code KRB5_CALLCONV krb5_krcc_lock static krb5_error_code KRB5_CALLCONV krb5_krcc_unlock (krb5_context context, krb5_ccache id); @@ -254,17 +253,6 @@ index fd1bcec..795ccd6 100644 +static krb5_error_code KRB5_CALLCONV krb5_krcc_switch_to +(krb5_context context, krb5_ccache cache); + -+/* Like k5memdup, but add a final null byte. */ -+static inline void * -+k5memdup0(const void *in, size_t len, krb5_error_code *code) -+{ -+ void *ptr = k5alloc(len + 1, code); -+ -+ if (ptr != NULL && len > 0) -+ memcpy(ptr, in, len); -+ return ptr; -+} -+ /* * Internal utility functions */ diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec index 8588d3c..08260a2 100644 --- a/SPECS/krb5.spec +++ b/SPECS/krb5.spec @@ -41,7 +41,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.11.3 -Release: 31%{?dist} +Release: 49%{?dist} # Maybe we should explode from the now-available-to-everybody tarball instead? # http://web.mit.edu/kerberos/dist/krb5/1.11/krb5-1.11.3-signed.tar Source0: krb5-%{version}.tar.gz @@ -68,6 +68,7 @@ Source34: kadmind.logrotate Source36: kpropd.init Source37: kadmind.init Source38: krb5kdc.init +Source39: krb5-krb5kdc.conf BuildRequires: cmake # Carry this locally until it's available in a packaged form. @@ -109,14 +110,47 @@ Patch136: krb5-1.11.3-prompter1.patch Patch137: krb5-1.11.3-prompter2.patch Patch138: krb5-1.11.3-gss-ccache-import.patch Patch139: krb5-1.10-CVE-2013-1418.patch +Patch140: krb5-CVE-2013-1417.patch +Patch141: krb5-1.11.3-client-loop.patch +Patch142: krb5-master-keyring-offsets.patch +Patch143: krb5-master-keyring-expiration.patch +Patch144: krb5-1.11-preauthcore.patch +Patch145: krb5-master-keyringccops.patch +Patch146: krb5-master-no-malloc0.patch +Patch147: krb5-master-ignore-empty-unnecessary-final-token.patch +Patch148: krb5-master-gss_oid_leak.patch +Patch149: krb5-master-keytab_close.patch +Patch150: krb5-1.11.3-copy_context.patch +Patch151: krb5-master-spnego_error_messages.patch +Patch152: krb5-master-keyring-kdcsync.patch + +Patch154: krb5-master-rcache-internal-const.patch +Patch155: krb5-master-rcache-acquirecred-cleanup.patch +Patch156: krb5-master-rcache-acquirecred-leak.patch +Patch157: krb5-1.11-rcache-acquirecred-source.patch +Patch158: krb5-master-empty-credstore.patch +Patch159: krb5-1.11.3-1.12.1-credstoretest.patch +Patch160: krb5-1.11-rcache-acquirecred-test.patch + +Patch161: krb5-1.11-spnego-preserve-oid.patch + +Patch162: krb5-1.12-pwdch-fast.patch + +Patch163: krb5-1.11.3-nodelete-pkinit.patch +Patch164: 0001-Don-t-leak-the-creds-we-don-t-plan-to-copy-over.patch +Patch165: 0003-Handlers-won-t-take-care-of-initializing-err.patch +Patch166: 0004-Don-t-leak-the-per-request-preauth-context.patch # Patches for otp plugin backport Patch201: krb5-1.11.2-keycheck.patch Patch202: krb5-1.11.2-otp.patch +Patch203: krb5-1.11.3-otp2.patch +Patch204: krb5-1.11.5-move-otp-sockets.patch # Patches for kernel-persistent-keyring support (backport) Patch301: persistent_keyring.patch Patch302: krb5-master-kinit-cccol.patch +Patch303: krb5-keyring-strtol.patch Patch400: 0000-ksu-intermediates.patch Patch401: 0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch @@ -174,6 +208,9 @@ BuildRequires: net-tools, rpcbind BuildRequires: hostname BuildRequires: iproute %endif +%if 0%{?fedora} >= 9 +BuildRequires: python-pyrad +%endif %if %{WITH_LDAP} BuildRequires: openldap-devel @@ -321,6 +358,7 @@ ln -s NOTICE LICENSE %patch301 -p1 -b .persistent-keyring %patch302 -p1 -b .kinit-cccol +%patch303 -p1 -b .keyring-strtol %patch400 -p1 -b .intermediates %patch401 -p1 -b .Don-t-try-to-stat-not-on-disk-ccache-residuals @@ -371,9 +409,39 @@ ln -s NOTICE LICENSE %patch137 -p1 -b .prompter2 %patch138 -p1 -b .gss-ccache-import %patch139 -p1 -b .CVE-2013-1418 +%patch140 -p1 -b .CVE-2013-1417 +%patch141 -p1 -b .client-loop +%patch142 -p1 -b .keyring-offsets +%patch143 -p1 -b .keyring-expiration +%patch144 -p0 -b .preauthcore +%patch145 -p1 -b .keyringccops +%patch146 -p1 -b .no-malloc0 +%patch147 -p1 -b .ignore-empty-unnecessary-final-token +%patch148 -p1 -b .gss_oid_leak +%patch149 -p1 -b .keytab_close +%patch150 -p1 -b .copy_context +%patch151 -p1 -b .spnego_error_messages +%patch152 -p1 -b .keyring-kdcsync + +%patch154 -p1 -b .rcache-internal-const +%patch155 -p1 -b .rcache-acquirecred-cleanup +%patch156 -p1 -b .rcache-acquirecred-leak +%patch157 -p1 -b .rcache-acquirecred-source +%patch158 -p1 -b .empty-credstore +%patch159 -p1 -b .credstoretest +%patch160 -p1 -b .rcache-acquirecred-test + +%patch161 -p1 -b .spnego-preserve-oid +%patch162 -p1 -b .pwdch-fast +%patch163 -p1 -b .nodelete-pkinit +%patch164 -p1 -b .Don-t-leak-the-creds-we-don-t-plan-to-copy-over +%patch165 -p1 -b .Handlers-won-t-take-care-of-initializing-err +%patch166 -p1 -b .Don-t-leak-the-per-request-preauth-context %patch201 -p1 -b .keycheck %patch202 -p1 -b .otp +%patch203 -p1 -b .otp2 +%patch204 -p1 -b .move-otp-sockets # Take the execute bit off of documentation. chmod -x doc/krb5-protocol/*.txt @@ -431,6 +499,9 @@ pushd src %if 0%{?compile_default_ccache_name} DEFCCNAME=%{compiled_default_ccache_name}; export DEFCCNAME %endif +# Set this so that configure will have a value even if the current version of +# autoconf doesn't set one. +runstatedir=%{_localstatedir}/run; export runstatedir # Work out the CFLAGS and CPPFLAGS which we intend to use. INCLUDES=-I%{_includedir}/et CFLAGS="`echo $RPM_OPT_FLAGS $DEFINES $INCLUDES -fPIC -fno-strict-aliasing -fstack-protector-all`" @@ -481,6 +552,13 @@ CPPFLAGS="`echo $DEFINES $INCLUDES`" make popd +# Sanity check the KDC_RUN_DIR. +configured_kdcrundir=`grep KDC_RUN_DIR src/include/osconf.h | awk '{print $NF}'` +configured_kdcrundir=`eval echo $configured_kdcrundir` +if test "$configured_kdcrundir" != %{_localstatedir}/run/krb5kdc ; then + exit 1 +fi + # Build the docs. make -C src/doc paths.py version.py cp src/doc/paths.py doc/ @@ -577,6 +655,9 @@ for wrapper in \ %{SOURCE8} ; do install -pm 755 ${wrapper} $RPM_BUILD_ROOT%{_sbindir}/ done +mkdir -p $RPM_BUILD_ROOT/%{_tmpfilesdir} +install -pm 644 %{SOURCE39} $RPM_BUILD_ROOT/%{_tmpfilesdir}/ +mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/run/krb5kdc %else mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d for init in \ @@ -850,6 +931,8 @@ exit 0 %{_unitdir}/krb5kdc.service %{_unitdir}/kadmin.service %{_unitdir}/kprop.service +%{_tmpfilesdir}/krb5-krb5kdc.conf +%dir %{_localstatedir}/run/krb5kdc %else /etc/rc.d/init.d/krb5kdc /etc/rc.d/init.d/kadmin @@ -1022,6 +1105,112 @@ exit 0 %{_sbindir}/uuserver %changelog +* Tue Mar 11 2014 Nalin Dahyabhai - 1.11.3-49 +- mark the pkinit module "nodelete" so that applications which cause it + to be loaded and unloaded repeatedly don't suffer from memory loss when + libcrypto is initialized, allocating memory in the process, and then + unloaded along with the module (most of #1063732) +- when copying creds from one ccache to another, if we single out one + server for which creds shouldn't be copied, don't leak the memory + that those creds use (a bit more of #1063732) +- fix an uninitialized variable warning that could, rarely, cause a + service-unavailable error to be reported when a server was merely + unreachable (spotted while chasing #1063732) +- don't leak the per-request context allocated by preauth modules (a + bit more of #1063732) + +* Wed Mar 5 2014 Nathaniel McCallum - 1.11.3-48 +- add Nathaniel's backported fix for to make password changes work properly + when performed while obtaining creds over FAST (RT#7868, #1072579) + +* Tue Feb 18 2014 Nalin Dahyabhai - 1.11.3-47 +- spnego: pull in patch from master to restore preserving the OID of the + mechanism the initiator requested when we have multiple OIDs for the same + mechanism, so that we reply using the same mechanism OID and the initiator + doesn't get confused (#1066002, RT#7858) + +* Mon Feb 10 2014 Nalin Dahyabhai - 1.11.3-46 +- pull in patch from master to move the default directory which the KDC uses + when computing the socket path for a local OTP daemon from the database + directory (/var/kerberos/krb5kdc) to the newly-added run directory + (/run/krb5kdc), in line with what we're expecting in 1.13 (RT#7859, more + of #1040056 as #1063905) +- add a tmpfiles.d configuration file to have /run/krb5kdc created at + boot-time +- own /var/run/krb5kdc + +* Fri Jan 24 2014 Daniel Mach - 1.11.3-45 +- Mass rebuild 2014-01-24 + +* Tue Jan 21 2014 Nalin Dahyabhai - 1.11.3-44 +- pull in and backport multiple changes to allow replay caches to be added to + a GSS credential store as "rcache"-type credentials (RT#7818/#7819/#7836, + #1056078/#1056080) + +* Fri Jan 17 2014 Nalin Dahyabhai - 1.11.3-43 +- switch to upstream's fix for #1030607 + +* Wed Jan 15 2014 Nalin Dahyabhai - 1.11.3-42 +- add proposed patch to fix the GSSAPI library's checks for expired + client creds in gss_init_sec_context() so that they work with keyring + caches (RT#7820, #1030607) + +* Fri Dec 27 2013 Daniel Mach - 1.11.3-41 +- Mass rebuild 2013-12-27 + +* Thu Dec 19 2013 Nalin Dahyabhai - 1.11.3-40 +- pull in fix from master to make reporting of errors encountered by + the SPNEGO mechanism work better (RT#7045, part of #1043962) + +* Thu Dec 19 2013 Nalin Dahyabhai - 1.11.3-39 +- backport fixes to krb5_copy_context (RT#7807, #1044735/#1044739) + +* Wed Dec 18 2013 Nalin Dahyabhai - 1.11.3-38 +- pull in fix from master to return a NULL pointer rather than allocating + zero bytes of memory if we read a zero-length input token (RT#7794, part of + #1043962) +- pull in fix from master to ignore an empty token from an acceptor if + we've already finished authenticating (RT#7797, part of #1043962) +- pull in fix from master to avoid a memory leak when a mechanism's + init_sec_context function fails (RT#7803, part of #1043962) +- pull in fix from master to avoid a memory leak in a couple of error + cases which could occur while obtaining acceptor credentials (RT#7805, part + of #1043962) + +* Tue Dec 17 2013 Nalin Dahyabhai - 1.11.3-37 +- backport additional changes to libkrad to make it function more like + the version in upstream 1.12, and a few things in the OTP plugin as + well (most visibly, that the secret that's shared with the RADIUS server + is read from a file rather than used directly) (#1040056) + +* Thu Dec 5 2013 Nalin Dahyabhai - 1.11.3-36 +- backport fix to avoid double-freeing in the client when we're configured + to use a clpreauth module that isn't actually a clpreauth module (#1035203) +- implement checks for the keyring ccache not existing in the keyring + get/set-flags methods, and set an error message if we fail to read + the principal name from a kernel ccache (#1034690) + +* Mon Nov 18 2013 Nalin Dahyabhai - 1.11.3-35 +- backport fix to not spin on a short read when reading the length of a + response over TCP (RT#7508, #1029674) +- pull in fix to store KDC time offsets in keyring credential caches (RT#7768, + #1030607) +- pull in fix to set expiration times on credentials stored in keyring + credential caches (RT#7769, #1031724) + +* Fri Nov 15 2013 Nalin Dahyabhai - 1.11.3-34 +- incorporate fix for a KDC NULL pointer dereference while handling referrals + (CVE-2013-1417, #1030745) + +* Tue Nov 12 2013 Nalin Dahyabhai - 1.11.3-33 +- also handle cache collections when performing heuristics to determine a + target principal name (#1030002, actually more of #1015559) + +* Tue Nov 12 2013 Nalin Dahyabhai - 1.11.3-32 +- check more thoroughly for errors when resolving KEYRING ccache names of type + "persistent", which should only have a numeric UID as the next part of the + name (#1029110) + * Fri Nov 8 2013 Nalin Dahyabhai - 1.11.3-31 - revise proposed changes for ksu to handle dropping privileges and cases where we didn't pick up creds from the source ccache @@ -1032,7 +1221,7 @@ exit 0 * Tue Nov 5 2013 Nalin Dahyabhai - 1.11.3-29 - incorporate upstream patch for remote crash of KDCs which serve multiple - realms simultaneously (#1026981, RT#7756, CVE-2013-1418) + realms simultaneously (#1026981, RT#7756, CVE-2013-1418/CVE-2013-6800) * Tue Nov 5 2013 Nalin Dahyabhai - 1.11.3-28 - drop patch to add additional access() checks to ksu - they add to breakage