diff --git a/.gitignore b/.gitignore index 41fdedf..b195da3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/gssproxy-0.4.1.tar.gz +SOURCES/gssproxy-0.7.0.tar.gz diff --git a/.gssproxy.metadata b/.gssproxy.metadata index 10c33c6..b056804 100644 --- a/.gssproxy.metadata +++ b/.gssproxy.metadata @@ -1 +1 @@ -665bb3925994a7411488931bdcedcdbfd1268977 SOURCES/gssproxy-0.4.1.tar.gz +6fe2fc0e6754b279af6d2a5b9443de5f20d62e5a SOURCES/gssproxy-0.7.0.tar.gz diff --git a/SOURCES/0001-Correct-handling-of-EINTR-on-read-write.patch b/SOURCES/0001-Correct-handling-of-EINTR-on-read-write.patch deleted file mode 100644 index a7a35d6..0000000 --- a/SOURCES/0001-Correct-handling-of-EINTR-on-read-write.patch +++ /dev/null @@ -1,41 +0,0 @@ -From df58bccfa5bfb42d7162f537c506a658729d4175 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Mon, 28 Sep 2015 19:59:59 -0400 -Subject: [PATCH] Correct handling of EINTR on read()/write() - -The common send/recv functions where zeroing the ret variable only -once causing a loop if EINTR as actually ever set. - -Signed-off-by: Robbie Harwood -Signed-off-by: Simo Sorce ---- - proxy/src/client/gpm_common.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/proxy/src/client/gpm_common.c b/proxy/src/client/gpm_common.c -index f0d5e1104aa5f7198a382e2533c1347045bb9d95..6d626e8bc41e2f0e3294d2ebaf563155d18a528f 100644 ---- a/proxy/src/client/gpm_common.c -+++ b/proxy/src/client/gpm_common.c -@@ -160,8 +160,8 @@ static int gpm_send_buffer(struct gpm_ctx *gpmctx, - - retry = false; - do { -- ret = 0; - do { -+ ret = 0; - wn = send(gpmctx->fd, &size, sizeof(uint32_t), MSG_NOSIGNAL); - if (wn == -1) { - ret = errno; -@@ -216,8 +216,8 @@ static int gpm_recv_buffer(struct gpm_ctx *gpmctx, - size_t pos; - int ret; - -- ret = 0; - do { -+ ret = 0; - rn = read(gpmctx->fd, &size, sizeof(uint32_t)); - if (rn == -1) { - ret = errno; --- -2.4.3 - diff --git a/SOURCES/Allow-connection-to-self-when-impersonator-set.patch b/SOURCES/Allow-connection-to-self-when-impersonator-set.patch new file mode 100644 index 0000000..cbfbeec --- /dev/null +++ b/SOURCES/Allow-connection-to-self-when-impersonator-set.patch @@ -0,0 +1,236 @@ +From 0e04be2c1398dac40c50910a59157eed0ad5a7e4 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Tue, 14 Mar 2017 10:43:17 -0400 +Subject: [PATCH] Allow connection to self when impersonator set + +If the target of a context establishment is the impersonator itself, +then allow it. This kind of context establishment is used by tools like +mod_auth_gssapi to be able to inspect the ticket just obtained via +impersonation and it is basically a noop as the acceptor and the +impersonator are the same entitiy. + +Signed-off-by: Simo Sorce +Reviewed-by: Robbie Harwood +Merges: #172 +(cherry picked from commit eada55e831d12b42d3be3a555ff4e133bed7f594) +--- + proxy/src/gp_creds.c | 57 ++++++++++++++++++++++++++++++++----- + proxy/src/gp_rpc_creds.h | 3 +- + proxy/src/gp_rpc_init_sec_context.c | 2 +- + proxy/tests/t_impersonate.py | 35 ++++++++++++++++------- + 4 files changed, 78 insertions(+), 19 deletions(-) + +diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c +index 95a1c48..7d89b06 100644 +--- a/proxy/src/gp_creds.c ++++ b/proxy/src/gp_creds.c +@@ -883,7 +883,8 @@ static uint32_t get_impersonator_name(uint32_t *min, gss_cred_id_t cred, + } + } else if (ret_maj == GSS_S_UNAVAILABLE) { + /* Not supported by krb5 library yet, fallback to raw krb5 calls */ +- /* TODO: Remove once we set a required dependency on MIT 1.15+ */ ++ /* TODO: Remove once we set a minimum required dependency on a ++ * release that supports this call */ + ret_maj = get_impersonator_fallback(&ret_min, cred, impersonator); + if (ret_maj == GSS_S_FAILURE) { + if (ret_min == KRB5_CC_NOTFOUND) { +@@ -899,9 +900,47 @@ done: + return ret_maj; + } + ++static uint32_t check_impersonator_name(uint32_t *min, ++ gss_name_t target_name, ++ const char *impersonator) ++{ ++ gss_name_t canon_name = NULL; ++ gss_buffer_desc buf; ++ uint32_t ret_maj = 0; ++ uint32_t ret_min = 0; ++ uint32_t discard; ++ bool match; ++ ++ ret_maj = gss_canonicalize_name(&discard, target_name, &gp_mech_krb5, ++ &canon_name); ++ if (ret_maj != GSS_S_COMPLETE) { ++ *min = ret_min; ++ return ret_maj; ++ } ++ ++ ret_maj = gss_display_name(&discard, canon_name, &buf, NULL); ++ gss_release_name(&discard, &canon_name); ++ if (ret_maj != GSS_S_COMPLETE) { ++ *min = ret_min; ++ return ret_maj; ++ } ++ ++ match = (strncmp(impersonator, buf.value, buf.length) == 0) && ++ (strlen(impersonator) == buf.length); ++ gss_release_buffer(&discard, &buf); ++ ++ *min = 0; ++ if (match) { ++ return GSS_S_COMPLETE; ++ } else { ++ return GSS_S_UNAUTHORIZED; ++ } ++} ++ + uint32_t gp_cred_allowed(uint32_t *min, + struct gp_call_ctx *gpcall, +- gss_cred_id_t cred) ++ gss_cred_id_t cred, ++ gss_name_t target_name) + { + char *impersonator = NULL; + uint32_t ret_maj = 0; +@@ -924,11 +963,11 @@ uint32_t gp_cred_allowed(uint32_t *min, + if (ret_maj) goto done; + + /* if we find an impersonator entry we bail as that is not authorized, +- * if it were then gpcall->service->allow_const_deleg would have caused +- * the ealier check to return GSS_S_COMPLETE already */ ++ * *unless* the target is the impersonator itself! If the operation ++ * were authorized then gpcall->service->allow_const_deleg would have ++ * caused the ealier check to return GSS_S_COMPLETE already */ + if (impersonator != NULL) { +- ret_min = 0; +- ret_maj = GSS_S_UNAUTHORIZED; ++ ret_maj = check_impersonator_name(&ret_min, target_name, impersonator); + } + + done: +@@ -937,7 +976,11 @@ done: + GPDEBUGN(2, "Unauthorized impersonator credentials detected\n"); + break; + case GSS_S_COMPLETE: +- GPDEBUGN(2, "No impersonator credentials detected\n"); ++ if (impersonator) { ++ GPDEBUGN(2, "Credentials allowed for 'self'\n"); ++ } else { ++ GPDEBUGN(2, "No impersonator credentials detected\n"); ++ } + break; + default: + GPDEBUG("Failure while checking credentials\n"); +diff --git a/proxy/src/gp_rpc_creds.h b/proxy/src/gp_rpc_creds.h +index 54fe482..c116e53 100644 +--- a/proxy/src/gp_rpc_creds.h ++++ b/proxy/src/gp_rpc_creds.h +@@ -34,7 +34,8 @@ uint32_t gp_add_krb5_creds(uint32_t *min, + + uint32_t gp_cred_allowed(uint32_t *min, + struct gp_call_ctx *gpcall, +- gss_cred_id_t cred); ++ gss_cred_id_t cred, ++ gss_name_t target_name); + + void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags); + +diff --git a/proxy/src/gp_rpc_init_sec_context.c b/proxy/src/gp_rpc_init_sec_context.c +index 767a3ff..413e2ec 100644 +--- a/proxy/src/gp_rpc_init_sec_context.c ++++ b/proxy/src/gp_rpc_init_sec_context.c +@@ -108,7 +108,7 @@ int gp_init_sec_context(struct gp_call_ctx *gpcall, + } + } + +- ret_maj = gp_cred_allowed(&ret_min, gpcall, ich); ++ ret_maj = gp_cred_allowed(&ret_min, gpcall, ich, target_name); + if (ret_maj) { + goto done; + } +diff --git a/proxy/tests/t_impersonate.py b/proxy/tests/t_impersonate.py +index 3e25962..29f9a41 100755 +--- a/proxy/tests/t_impersonate.py ++++ b/proxy/tests/t_impersonate.py +@@ -34,19 +34,20 @@ IMPERSONATE_CONF_TEMPLATE = ''' + + ''' + +-def run_cmd(testdir, env, conf, name, socket, cmd, expected_failure): ++def run_cmd(testdir, env, conf, name, socket, cmd, keytab, expected_failure): + + logfile = conf['logfile'] + testenv = env.copy() + testenv.update({'KRB5CCNAME': os.path.join(testdir, 't' + conf['prefix'] + + '_impersonate.ccache'), +- 'KRB5_KTNAME': os.path.join(testdir, PROXY_KTNAME), ++ 'KRB5_KTNAME': os.path.join(testdir, keytab), + 'KRB5_TRACE': os.path.join(testdir, 't' + conf['prefix'] + + '_impersonate.trace'), + 'GSS_USE_PROXY': 'yes', + 'GSSPROXY_SOCKET': socket, + 'GSSPROXY_BEHAVIOR': 'REMOTE_FIRST'}) + ++ print("\nTesting: [%s]" % (name,), file=logfile) + print("[COMMAND]\n%s\n[ENVIRONMENT]\n%s\n" % (cmd, testenv), file=logfile) + logfile.flush() + +@@ -74,45 +75,59 @@ def run(testdir, env, conf): + rets = [] + + # Test all permitted ++ msg = "Impersonate" + socket = os.path.join(testdir, 'impersonate.socket') + cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, + path_prefix + 'impersonate.cache'] +- r = run_cmd(testdir, env, conf, "Impersonate", socket, cmd, False) ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, False) + rets.append(r) + +- #Test fail ++ #Test self fail ++ msg = "Impersonate fail self" + socket = os.path.join(testdir, 'impersonate-proxyonly.socket') + cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, + path_prefix + 'impersonate.cache'] +- r = run_cmd(testdir, env, conf, "Impersonate fail self", socket, cmd, True) ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, True) + rets.append(r) + +- #Test fail ++ #Test proxy fail ++ msg = "Impersonate fail proxy" + socket = os.path.join(testdir, 'impersonate-selfonly.socket') + cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, + path_prefix + 'impersonate.cache'] +- r = run_cmd(testdir, env, conf, "Impersonate fail proxy", socket, cmd, True) ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, True) + rets.append(r) + + #Test s4u2self half succeed ++ msg = "s4u2self delegation" + socket = os.path.join(testdir, 'impersonate-selfonly.socket') + cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, + path_prefix + 'impersonate.cache', 's4u2self'] +- r = run_cmd(testdir, env, conf, "s4u2self delegation", socket, cmd, False) ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, False) ++ rets.append(r) ++ ++ #Test proxy to self succeed ++ msg = "Impersonate to self" ++ socket = os.path.join(testdir, 'impersonate-selfonly.socket') ++ cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, HOST_GSS, ++ path_prefix + 'impersonate.cache', 's4u2proxy'] ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, SVC_KTNAME, False) + rets.append(r) + + #Test s4u2proxy half fail ++ msg = "s4u2proxy fail" + socket = os.path.join(testdir, 'impersonate-selfonly.socket') + cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, + path_prefix + 'impersonate.cache', 's4u2proxy'] +- r = run_cmd(testdir, env, conf, "s4u2proxy fail", socket, cmd, True) ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, True) + rets.append(r) + + #Test s4u2proxy half succeed ++ msg = "s4u2proxy" + socket = os.path.join(testdir, 'impersonate-proxyonly.socket') + cmd = ["./tests/t_impersonate", USR_NAME, HOST_GSS, PROXY_GSS, + path_prefix + 'impersonate.cache', 's4u2proxy'] +- r = run_cmd(testdir, env, conf, "s4u2proxy", socket, cmd, False) ++ r = run_cmd(testdir, env, conf, msg, socket, cmd, PROXY_KTNAME, False) + rets.append(r) + + # Reset back gssproxy conf diff --git a/SOURCES/Change-impersonator-check-code.patch b/SOURCES/Change-impersonator-check-code.patch new file mode 100644 index 0000000..9a29862 --- /dev/null +++ b/SOURCES/Change-impersonator-check-code.patch @@ -0,0 +1,216 @@ +From 37d1667ad0cc91f46a493281e62775cc8bbe3b5b Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Tue, 14 Mar 2017 10:20:08 -0400 +Subject: [PATCH] Change impersonator check code + +In MIT 1.15 we now have a native way to check for an impersonator, +implement the use of that function but still keep the fallback for +earlier krb5 versions that do not support this method for now. + +Signed-off-by: Simo Sorce +Reviewed-by: Robbie Harwood +Merges: #172 +(cherry picked from commit 73b50c0b2799f0aed53337a6516b8e1a27279ebf) +--- + proxy/configure.ac | 3 ++ + proxy/src/gp_creds.c | 147 ++++++++++++++++++++++++++++++++++++++------------- + 2 files changed, 112 insertions(+), 38 deletions(-) + +diff --git a/proxy/configure.ac b/proxy/configure.ac +index 63c0edf..c52dbb6 100644 +--- a/proxy/configure.ac ++++ b/proxy/configure.ac +@@ -131,6 +131,9 @@ AC_CHECK_LIB(gssapi_krb5, gss_export_cred,, + [AC_MSG_ERROR([GSSAPI library does not support gss_export_cred])], + [$GSSAPI_LIBS]) + ++AC_CHECK_DECLS([GSS_KRB5_GET_CRED_IMPERSONATOR], [], [], ++ [[#include ]]) ++ + AC_SUBST([KRB5_CFLAGS]) + AC_SUBST([KRB5_LIBS]) + AC_SUBST([GSSAPI_CFLAGS]) +diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c +index 171a724..95a1c48 100644 +--- a/proxy/src/gp_creds.c ++++ b/proxy/src/gp_creds.c +@@ -773,9 +773,9 @@ void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags) + *flags &= ~gpcall->service->filter_flags; + } + +-uint32_t gp_cred_allowed(uint32_t *min, +- struct gp_call_ctx *gpcall, +- gss_cred_id_t cred) ++ ++static uint32_t get_impersonator_fallback(uint32_t *min, gss_cred_id_t cred, ++ char **impersonator) + { + uint32_t ret_maj = 0; + uint32_t ret_min = 0; +@@ -785,22 +785,6 @@ uint32_t gp_cred_allowed(uint32_t *min, + krb5_data config; + int err; + +- if (cred == GSS_C_NO_CREDENTIAL) { +- return GSS_S_CRED_UNAVAIL; +- } +- +- if (gpcall->service->trusted || +- gpcall->service->impersonate || +- gpcall->service->allow_const_deleg) { +- +- GPDEBUGN(2, "Credentials allowed by configuration\n"); +- *min = 0; +- return GSS_S_COMPLETE; +- } +- +- /* FIXME: krb5 specific code, should get an oid registerd to query the +- * cred with gss_inquire_cred_by_oid() or similar instead */ +- + err = krb5_init_context(&context); + if (err) { + ret_min = err; +@@ -835,21 +819,116 @@ uint32_t gp_cred_allowed(uint32_t *min, + goto done; + } + ++ err = krb5_cc_get_config(context, ccache, NULL, "proxy_impersonator", ++ &config); ++ if (err == 0) { ++ *impersonator = strndup(config.data, config.length); ++ if (!*impersonator) { ++ ret_min = ENOMEM; ++ ret_maj = GSS_S_FAILURE; ++ } else { ++ ret_min = 0; ++ ret_maj = GSS_S_COMPLETE; ++ } ++ krb5_free_data_contents(context, &config); ++ } else { ++ ret_min = err; ++ ret_maj = GSS_S_FAILURE; ++ } ++ ++done: ++ if (context) { ++ if (ccache) { ++ krb5_cc_destroy(context, ccache); ++ } ++ krb5_free_context(context); ++ } ++ free(memcache); ++ ++ *min = ret_min; ++ return ret_maj; ++} ++ ++#if !HAVE_DECL_GSS_KRB5_GET_CRED_IMPERSONATOR ++gss_OID_desc impersonator_oid = { ++ 11, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0e") ++}; ++const gss_OID GSS_KRB5_GET_CRED_IMPERSONATOR = &impersonator_oid; ++#endif ++ ++static uint32_t get_impersonator_name(uint32_t *min, gss_cred_id_t cred, ++ char **impersonator) ++{ ++ gss_buffer_set_t bufset = GSS_C_NO_BUFFER_SET; ++ uint32_t ret_maj = 0; ++ uint32_t ret_min = 0; ++ uint32_t discard; ++ ++ *impersonator = NULL; ++ ++ ret_maj = gss_inquire_cred_by_oid(&ret_min, cred, ++ GSS_KRB5_GET_CRED_IMPERSONATOR, ++ &bufset); ++ if (ret_maj == GSS_S_COMPLETE) { ++ if (bufset->count == 0) { ++ ret_min = ENOENT; ++ ret_maj = GSS_S_COMPLETE; ++ goto done; ++ } ++ *impersonator = strndup(bufset->elements[0].value, ++ bufset->elements[0].length); ++ if (!*impersonator) { ++ ret_min = ENOMEM; ++ ret_maj = GSS_S_FAILURE; ++ } ++ } else if (ret_maj == GSS_S_UNAVAILABLE) { ++ /* Not supported by krb5 library yet, fallback to raw krb5 calls */ ++ /* TODO: Remove once we set a required dependency on MIT 1.15+ */ ++ ret_maj = get_impersonator_fallback(&ret_min, cred, impersonator); ++ if (ret_maj == GSS_S_FAILURE) { ++ if (ret_min == KRB5_CC_NOTFOUND) { ++ ret_min = ENOENT; ++ ret_maj = GSS_S_COMPLETE; ++ } ++ } ++ } ++ ++done: ++ (void)gss_release_buffer_set(&discard, &bufset); ++ *min = ret_min; ++ return ret_maj; ++} ++ ++uint32_t gp_cred_allowed(uint32_t *min, ++ struct gp_call_ctx *gpcall, ++ gss_cred_id_t cred) ++{ ++ char *impersonator = NULL; ++ uint32_t ret_maj = 0; ++ uint32_t ret_min = 0; ++ ++ if (cred == GSS_C_NO_CREDENTIAL) { ++ return GSS_S_CRED_UNAVAIL; ++ } ++ ++ if (gpcall->service->trusted || ++ gpcall->service->impersonate || ++ gpcall->service->allow_const_deleg) { ++ ++ GPDEBUGN(2, "Credentials allowed by configuration\n"); ++ *min = 0; ++ return GSS_S_COMPLETE; ++ } ++ ++ ret_maj = get_impersonator_name(&ret_min, cred, &impersonator); ++ if (ret_maj) goto done; ++ + /* if we find an impersonator entry we bail as that is not authorized, + * if it were then gpcall->service->allow_const_deleg would have caused + * the ealier check to return GSS_S_COMPLETE already */ +- err = krb5_cc_get_config(context, ccache, NULL, "proxy_impersonator", +- &config); +- if (!err) { +- krb5_free_data_contents(context, &config); ++ if (impersonator != NULL) { + ret_min = 0; + ret_maj = GSS_S_UNAUTHORIZED; +- } else if (err != KRB5_CC_NOTFOUND) { +- ret_min = err; +- ret_maj = GSS_S_FAILURE; +- } else { +- ret_min = 0; +- ret_maj = GSS_S_COMPLETE; + } + + done: +@@ -864,15 +943,7 @@ done: + GPDEBUG("Failure while checking credentials\n"); + break; + } +- if (context) { +- /* NOTE: destroy only if we created a MEMORY ccache */ +- if (ccache) { +- if (memcache) krb5_cc_destroy(context, ccache); +- else krb5_cc_close(context, ccache); +- } +- krb5_free_context(context); +- } +- free(memcache); ++ free(impersonator); + *min = ret_min; + return ret_maj; + } diff --git a/SOURCES/Make-proc-file-failure-loud-but-nonfatal.patch b/SOURCES/Make-proc-file-failure-loud-but-nonfatal.patch new file mode 100644 index 0000000..84d9a83 --- /dev/null +++ b/SOURCES/Make-proc-file-failure-loud-but-nonfatal.patch @@ -0,0 +1,75 @@ +From b52f4f84531ee065f9553b9ecc5be53c89103800 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Thu, 25 May 2017 13:06:17 -0400 +Subject: [PATCH] Make proc file failure loud but nonfatal + +Signed-off-by: Robbie Harwood +Reviewed-by: Simo Sorce +Resolves: #190 +(cherry picked from commit 4f60bf02a1a68cbb26251e764357b753f80790f3) +--- + proxy/src/gp_init.c | 34 +++++++++++++++------------------- + 1 file changed, 15 insertions(+), 19 deletions(-) + +diff --git a/proxy/src/gp_init.c b/proxy/src/gp_init.c +index bb7ba6b..d367f92 100644 +--- a/proxy/src/gp_init.c ++++ b/proxy/src/gp_init.c +@@ -144,11 +144,11 @@ void init_proc_nfsd(struct gp_config *cfg) + { + char buf[] = "1"; + bool enabled = false; +- int fd, i, ret; ++ int fd, ret; + + /* check first if any service enabled kernel support */ +- for (i = 0; i < cfg->num_svcs; i++) { +- if (cfg->svcs[i]->kernel_nfsd == true) { ++ for (int i = 0; i < cfg->num_svcs; i++) { ++ if (cfg->svcs[i]->kernel_nfsd) { + enabled = true; + break; + } +@@ -161,30 +161,26 @@ void init_proc_nfsd(struct gp_config *cfg) + fd = open(LINUX_PROC_USE_GSS_PROXY_FILE, O_RDWR); + if (fd == -1) { + ret = errno; +- fprintf(stderr, "GSS-Proxy is not supported by this kernel since " +- "file %s could not be found: %d (%s)\n", +- LINUX_PROC_USE_GSS_PROXY_FILE, +- ret, gp_strerror(ret)); +- exit(1); ++ GPDEBUG("Kernel doesn't support GSS-Proxy (can't open %s: %d (%s))\n", ++ LINUX_PROC_USE_GSS_PROXY_FILE, ret, gp_strerror(ret)); ++ goto fail; + } + + ret = write(fd, buf, 1); + if (ret != 1) { + ret = errno; +- fprintf(stderr, "Failed to write to %s: %d (%s)\n", +- LINUX_PROC_USE_GSS_PROXY_FILE, +- ret, gp_strerror(ret)); +- exit(1); ++ GPDEBUG("Failed to write to %s: %d (%s)\n", ++ LINUX_PROC_USE_GSS_PROXY_FILE, ret, gp_strerror(ret)); + } + +- ret = close(fd); +- if (ret == -1) { +- ret = errno; +- fprintf(stderr, "Failed to close %s: %d (%s)\n", +- LINUX_PROC_USE_GSS_PROXY_FILE, +- ret, gp_strerror(ret)); +- exit(1); ++ close(fd); ++ if (ret != 0) { ++ goto fail; + } ++ ++ return; ++fail: ++ GPDEBUG("Problem with kernel communication! NFS server will not work\n"); + } + + void write_pid(void) diff --git a/SOURCES/Properly-renew-expired-credentials.patch b/SOURCES/Properly-renew-expired-credentials.patch new file mode 100644 index 0000000..a4b3005 --- /dev/null +++ b/SOURCES/Properly-renew-expired-credentials.patch @@ -0,0 +1,75 @@ +From fc748ba83eb29f10fd44b6572b04709fa27dc587 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Mon, 13 Mar 2017 08:06:12 -0400 +Subject: [PATCH] Properly renew expired credentials + +When a caller imports expired credentials, we aim to actually renew them +if we can. However due to incorrect checks and not clearing of the +ret_maj variable after checks we end up returning an error instead. + +Also fix mechglue to also save and properly report the first call errors +when both remote and local fail. + +Resolves: #170 + +Signed-off-by: Simo Sorce +Reviewed-by: Robbie Harwood +(cherry picked from commit dc462321226f59ceaab0d3db47446a694a8ecba2) +--- + proxy/src/gp_creds.c | 14 +++++++++----- + proxy/src/mechglue/gpp_acquire_cred.c | 5 +++++ + 2 files changed, 14 insertions(+), 5 deletions(-) + +diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c +index 5d84904..171a724 100644 +--- a/proxy/src/gp_creds.c ++++ b/proxy/src/gp_creds.c +@@ -629,8 +629,12 @@ uint32_t gp_add_krb5_creds(uint32_t *min, + ret_maj = gp_check_cred(&ret_min, in_cred, desired_name, cred_usage); + if (ret_maj == GSS_S_COMPLETE) { + return GSS_S_COMPLETE; +- } else if (ret_maj != GSS_S_CREDENTIALS_EXPIRED && +- ret_maj != GSS_S_NO_CRED) { ++ } else if (ret_maj == GSS_S_CREDENTIALS_EXPIRED || ++ ret_maj == GSS_S_NO_CRED) { ++ /* continue and try to obtain new creds */ ++ ret_maj = 0; ++ ret_min = 0; ++ } else { + *min = ret_min; + return GSS_S_CRED_UNAVAIL; + } +@@ -639,14 +643,14 @@ uint32_t gp_add_krb5_creds(uint32_t *min, + if (acquire_type == ACQ_NORMAL) { + ret_min = gp_get_cred_environment(gpcall, desired_name, &req_name, + &cred_usage, &cred_store); ++ if (ret_min) { ++ ret_maj = GSS_S_CRED_UNAVAIL; ++ } + } else if (desired_name) { + ret_maj = gp_conv_gssx_to_name(&ret_min, desired_name, &req_name); + } + if (ret_maj) { + goto done; +- } else if (ret_min) { +- ret_maj = GSS_S_CRED_UNAVAIL; +- goto done; + } + + if (!try_impersonate(gpcall->service, cred_usage, acquire_type)) { +diff --git a/proxy/src/mechglue/gpp_acquire_cred.c b/proxy/src/mechglue/gpp_acquire_cred.c +index d876699..514fdd1 100644 +--- a/proxy/src/mechglue/gpp_acquire_cred.c ++++ b/proxy/src/mechglue/gpp_acquire_cred.c +@@ -186,6 +186,11 @@ OM_uint32 gssi_acquire_cred_from(OM_uint32 *minor_status, + } + + if (behavior == GPP_REMOTE_FIRST) { ++ if (maj != GSS_S_COMPLETE) { ++ /* save errors */ ++ tmaj = maj; ++ tmin = min; ++ } + /* So remote failed, but we can fallback to local, try that */ + maj = acquire_local(&min, NULL, name, + time_req, desired_mechs, cred_usage, cred_store, diff --git a/SOURCES/gssproxy-0.5.1-socket_permission_checking.patch b/SOURCES/gssproxy-0.5.1-socket_permission_checking.patch deleted file mode 100644 index 1aa84b2..0000000 --- a/SOURCES/gssproxy-0.5.1-socket_permission_checking.patch +++ /dev/null @@ -1,73 +0,0 @@ -From bbda272145ebbe0cbb65467c1573e583b9e1b7c7 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Fri, 3 Jun 2016 14:30:36 +0000 -Subject: [PATCH] Use new socket if uid, pid, or gid changes - -The gssproxy daemon uses SO_PEERCRED to determine credentials of the -connecting process. However, these credentials are set only at the time -connect has called. Therefore they must be reset every time uid or pid -changes. For completeness, we check gid as well. - -Signed-off-by: Robbie Harwood -Reviewed-by: Simo Sorce -Closes #27 ---- - proxy/src/client/gpm_common.c | 22 ++++++++++++++++++++++ - 1 file changed, 22 insertions(+) - -diff --git a/proxy/src/client/gpm_common.c b/proxy/src/client/gpm_common.c -index cb4ccdb..0a54dbc 100644 ---- a/proxy/src/client/gpm_common.c -+++ b/proxy/src/client/gpm_common.c -@@ -13,6 +13,12 @@ - struct gpm_ctx { - pthread_mutex_t lock; - int fd; -+ -+ /* these are only meaningful if fd != -1 */ -+ pid_t pid; -+ uid_t uid; -+ gid_t gid; -+ - int next_xid; - }; - -@@ -93,6 +99,9 @@ done: - } - } - gpmctx->fd = fd; -+ gpmctx->pid = getpid(); -+ gpmctx->uid = geteuid(); -+ gpmctx->gid = getegid(); - return ret; - } - -@@ -120,12 +129,25 @@ static void gpm_close_socket(struct gpm_ctx *gpmctx) - static int gpm_grab_sock(struct gpm_ctx *gpmctx) - { - int ret; -+ pid_t p; -+ uid_t u; -+ gid_t g; - - ret = pthread_mutex_lock(&gpmctx->lock); - if (ret) { - return ret; - } - -+ /* Detect fork / setresuid and friends */ -+ p = getpid(); -+ u = geteuid(); -+ g = getegid(); -+ -+ if (gpmctx->fd != -1 && -+ (p != gpmctx->pid || u != gpmctx->uid || g != gpmctx->gid)) { -+ gpm_close_socket(gpmctx); -+ } -+ - if (gpmctx->fd == -1) { - ret = gpm_open_socket(gpmctx); - } --- -2.8.1 - diff --git a/SOURCES/gssproxy_ticket_140_0001-bug-140-Remove-fno-strict-aliasing.patch b/SOURCES/gssproxy_ticket_140_0001-bug-140-Remove-fno-strict-aliasing.patch deleted file mode 100644 index b64110a..0000000 --- a/SOURCES/gssproxy_ticket_140_0001-bug-140-Remove-fno-strict-aliasing.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 21cde7fde306b3a4699fff415c90df457f5b30c7 Mon Sep 17 00:00:00 2001 -From: Roland Mainz -Date: Thu, 9 Apr 2015 19:56:45 +0200 -Subject: [PATCH] bug #140: Remove -fno-strict-aliasing - -Remove -fno-strict-aliasing (this is not required because gssproxy -is mostly a good ISO C99/C11 citizen) and replace it with --Werror=strict-aliasing to ensure that if *anything* creeps up -the build will just fail (this requires in gcc4.x's case the -use of -fstrict-aliasing, too). - -References: - - http://dbp-consulting.com/tutorials/StrictAliasing.html - -Signed-off-by: Roland Mainz ---- - proxy/Makefile.am | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/proxy/Makefile.am b/proxy/Makefile.am -index 1e7c39cf2c2d3ef2c96c1b28c76d53d378054da6..6e38cf8116b70d51fe873720a2205572402d9f99 100644 ---- a/proxy/Makefile.am -+++ b/proxy/Makefile.am -@@ -36,10 +36,12 @@ if WANT_AUX_INFO - AM_CFLAGS += -aux-info $@.X - endif - if HAVE_GCC -+# -fstrict-aliasing is needed so that -W*strict-aliasing works -+# properly - AM_CFLAGS += -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith \ - -Wcast-qual -Wcast-align -Wwrite-strings \ -- -Werror-implicit-function-declaration \ -- -fno-strict-aliasing -+ -fstrict-aliasing -Wstrict-aliasing -Werror=strict-aliasing \ -+ -Werror-implicit-function-declaration - endif - - dist_pkgconfig_DATA = --- -1.9.3 - diff --git a/SOURCES/gssproxy_ticket_143_workaround_Service-HTTP.patch b/SOURCES/gssproxy_ticket_143_workaround_Service-HTTP.patch deleted file mode 100644 index 9e8fdbe..0000000 --- a/SOURCES/gssproxy_ticket_143_workaround_Service-HTTP.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/proxy/examples/gssproxy.conf.in b/proxy/examples/gssproxy.conf.in -index 321a4e7..5dd708b 100644 ---- a/proxy/examples/gssproxy.conf.in -+++ b/proxy/examples/gssproxy.conf.in -@@ -1,5 +1,11 @@ - [gssproxy] - -+[service/HTTP] -+ mechs = krb5 -+ cred_store = keytab:/etc/gssproxy/http.keytab -+ cred_store = ccache:/var/lib/gssproxy/clients/krb5cc_%U -+ euid = 48 -+ - [service/nfs-server] - mechs = krb5 - socket = /run/gssproxy.sock diff --git a/SOURCES/gssproxy_ticket_145_130-Set-default-rcache.patch b/SOURCES/gssproxy_ticket_145_130-Set-default-rcache.patch deleted file mode 100644 index 368305c..0000000 --- a/SOURCES/gssproxy_ticket_145_130-Set-default-rcache.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/proxy/systemd/gssproxy.service.in b/systemd/gssproxy.service.in -index 7aa6785..dae39ee 100644 ---- a/proxy/systemd/gssproxy.service.in -+++ b/proxy/systemd/gssproxy.service.in -@@ -6,6 +6,7 @@ Before=nfs-secure.service nfs-secure-server.service - Requires=proc-fs-nfsd.mount - - [Service] -+Environment=KRB5RCACHEDIR=/var/lib/gssproxy/rcache - ExecStart=@sbindir@/gssproxy -D - # These two should be used with traditional UNIX forking daemons - # consult systemd.service(5) for more details diff --git a/SOURCES/gssproxy_ticket_155-krb5_principal.patch b/SOURCES/gssproxy_ticket_155-krb5_principal.patch deleted file mode 100644 index 04ca732..0000000 --- a/SOURCES/gssproxy_ticket_155-krb5_principal.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 41c8b2631fdd09b1e97e341838c71ffd11033133 Mon Sep 17 00:00:00 2001 -From: Simo Sorce -Date: Fri, 15 Apr 2016 12:04:48 -0400 -Subject: [PATCH] Implement the krb5_principal option - -The krb5_principal option was defined and documented but not actually used. -Implement it's use when a service keytab is provided. - -Ticket: https://fedorahosted.org/gss-proxy/ticket/155 - -Signed-off-by: Simo Sorce -Reviewed-by: Robbie Harwood - -[rharwood@redhat.com adjust macros for backport] ---- - proxy/src/gp_creds.c | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - -diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c -index 255200c..551b020 100644 ---- a/proxy/src/gp_creds.c -+++ b/proxy/src/gp_creds.c -@@ -325,6 +325,23 @@ static int gp_get_cred_environment(struct gp_call_ctx *gpcall, - } - } - -+ if (use_service_keytab && -+ (*requested_name == GSS_C_NO_NAME) && (svc->krb5.principal)) { -+ /* configuration dictates to use a specific name */ -+ gss_buffer_desc const_buf; -+ const_buf.value = svc->krb5.principal; -+ const_buf.length = strlen(svc->krb5.principal) + 1; -+ -+ ret_maj = gss_import_name(&ret_min, &const_buf, -+ (void *)(uintptr_t)GSS_KRB5_NT_PRINCIPAL_NAME, -+ requested_name); -+ if (ret_maj) { -+ GPERROR("Failed to import krb5_principal name %s\n", -+ svc->krb5.principal); -+ goto done; -+ } -+ } -+ - if (svc->krb5.cred_store == NULL) { - return 0; - } --- -2.8.1 - diff --git a/SOURCES/krb5-1.14-inquire_attrs_accept_null.patch b/SOURCES/krb5-1.14-inquire_attrs_accept_null.patch deleted file mode 100644 index 97cd609..0000000 --- a/SOURCES/krb5-1.14-inquire_attrs_accept_null.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 14e33b725c991d6c500ca93e241ed64e1a755843 Mon Sep 17 00:00:00 2001 -From: Robbie Harwood -Date: Wed, 16 Dec 2015 17:48:11 -0500 -Subject: [PATCH 2/2] Fix for gss_inquire_attrs_for_mech accepting NULLs - -As per rfc5587, gss_inquire_attrs_for_mech must accept NULL mech_attrs -and known_mech_attrs arguments. Up until 1.14, MIT krb5 was not ever -passing NULLs in these fields. - -This fixes an interposer loop (and subsequent segmentation fault) due -to our previous assumption that these arguments not be NULL. - -See also: https://tools.ietf.org/html/rfc5587#section-3.4.3 - -Signed-off-by: Robbie Harwood -Reviewed-by: Simo Sorce ---- - proxy/src/client/gpm_indicate_mechs.c | 38 ++++++++++++++++++++--------------- - 1 file changed, 22 insertions(+), 16 deletions(-) - -diff --git a/proxy/src/client/gpm_indicate_mechs.c b/proxy/src/client/gpm_indicate_mechs.c -index 35ce3bb..d4df923 100644 ---- a/proxy/src/client/gpm_indicate_mechs.c -+++ b/proxy/src/client/gpm_indicate_mechs.c -@@ -444,10 +444,6 @@ OM_uint32 gpm_inquire_attrs_for_mech(OM_uint32 *minor_status, - if (!minor_status) { - return GSS_S_CALL_INACCESSIBLE_WRITE; - } -- if (!mech_attrs || !known_mech_attrs) { -- *minor_status = 0; -- return GSS_S_CALL_INACCESSIBLE_WRITE; -- } - - ret_min = gpmint_init_global_mechs(); - if (ret_min) { -@@ -459,21 +455,31 @@ OM_uint32 gpm_inquire_attrs_for_mech(OM_uint32 *minor_status, - if (!gpm_equal_oids(global_mechs.info[i].mech, mech)) { - continue; - } -- ret_maj = gpm_copy_gss_OID_set(&ret_min, -- global_mechs.info[i].mech_attrs, -- mech_attrs); -- if (ret_maj) { -+ -+ if (mech_attrs != NULL) { -+ ret_maj = gpm_copy_gss_OID_set(&ret_min, -+ global_mechs.info[i].mech_attrs, -+ mech_attrs); -+ if (ret_maj) { -+ *minor_status = ret_min; -+ return ret_maj; -+ } -+ } -+ -+ if (known_mech_attrs != NULL) { -+ ret_maj = gpm_copy_gss_OID_set(&ret_min, -+ global_mechs.info[i].known_mech_attrs, -+ known_mech_attrs); -+ if (ret_maj) { -+ gss_release_oid_set(&discard, known_mech_attrs); -+ } - *minor_status = ret_min; - return ret_maj; - } -- ret_maj = gpm_copy_gss_OID_set(&ret_min, -- global_mechs.info[i].known_mech_attrs, -- known_mech_attrs); -- if (ret_maj) { -- gss_release_oid_set(&discard, known_mech_attrs); -- } -- *minor_status = ret_min; -- return ret_maj; -+ -+ /* all requested attributes copied successfully */ -+ *minor_status = 0; -+ return GSS_S_COMPLETE; - } - - *minor_status = 0; --- -2.6.4 - diff --git a/SOURCES/krb5-1.14-inquire_context_no_name.patch b/SOURCES/krb5-1.14-inquire_context_no_name.patch deleted file mode 100644 index 865d301..0000000 --- a/SOURCES/krb5-1.14-inquire_context_no_name.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 14ecfa9fe9e843bdb2eb09c60a5ec592c8de4cdc Mon Sep 17 00:00:00 2001 -From: Simo Sorce -Date: Mon, 14 Dec 2015 17:38:36 -0500 -Subject: [PATCH 1/2] Since krb5 1.14 inquire_context may return no name - -In 1.14 a patch to more officially support partially established contexts -has been intrdouced. With this patch names are not returned. - -Cope with that by checking if a name is provided before trying to convert. - -Signed-off-by: Simo Sorce -Reviewed-by: Robbie Harwood ---- - proxy/src/gp_export.c | 16 ++++++++++------ - 1 file changed, 10 insertions(+), 6 deletions(-) - -diff --git a/proxy/src/gp_export.c b/proxy/src/gp_export.c -index 0ef3128..3b9a23b 100644 ---- a/proxy/src/gp_export.c -+++ b/proxy/src/gp_export.c -@@ -526,14 +526,18 @@ uint32_t gp_export_ctx_id_to_gssx(uint32_t *min, int type, gss_OID mech, - goto done; - } - -- ret_maj = gp_conv_name_to_gssx(&ret_min, src_name, &out->src_name); -- if (ret_maj) { -- goto done; -+ if (src_name != GSS_C_NO_NAME) { -+ ret_maj = gp_conv_name_to_gssx(&ret_min, src_name, &out->src_name); -+ if (ret_maj) { -+ goto done; -+ } - } - -- ret_maj = gp_conv_name_to_gssx(&ret_min, targ_name, &out->targ_name); -- if (ret_maj) { -- goto done; -+ if (targ_name != GSS_C_NO_NAME) { -+ ret_maj = gp_conv_name_to_gssx(&ret_min, targ_name, &out->targ_name); -+ if (ret_maj) { -+ goto done; -+ } - } - - out->lifetime = lifetime_rec; --- -2.6.4 - diff --git a/SPECS/gssproxy.spec b/SPECS/gssproxy.spec index 0d5670c..74513bd 100644 --- a/SPECS/gssproxy.spec +++ b/SPECS/gssproxy.spec @@ -1,12 +1,12 @@ Name: gssproxy -Version: 0.4.1 -Release: 13%{?dist} +Version: 0.7.0 +Release: 4%{?dist} Summary: GSSAPI Proxy Group: System Environment/Libraries License: MIT -URL: http://fedorahosted.org/gss-proxy -Source0: http://fedorahosted.org/released/gss-proxy/%{name}-%{version}.tar.gz +URL: https://pagure.io/gssproxy +Source0: https://releases.pagure.org/gssproxy/gssproxy-%{version}.tar.gz BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %global servicename gssproxy @@ -14,20 +14,16 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %global gpstatedir %{_localstatedir}/lib/gssproxy ### Patches ### -Patch1: gssproxy_ticket_140_0001-bug-140-Remove-fno-strict-aliasing.patch -Patch3: gssproxy_ticket_145_130-Set-default-rcache.patch -Patch4: gssproxy_ticket_143_workaround_Service-HTTP.patch -Patch5: 0001-Correct-handling-of-EINTR-on-read-write.patch -Patch6: krb5-1.14-inquire_context_no_name.patch -Patch7: krb5-1.14-inquire_attrs_accept_null.patch -Patch8: gssproxy-0.5.1-socket_permission_checking.patch -Patch9: gssproxy_ticket_155-krb5_principal.patch +Patch0: Properly-renew-expired-credentials.patch +Patch1: Change-impersonator-check-code.patch +Patch2: Allow-connection-to-self-when-impersonator-set.patch +Patch3: Make-proc-file-failure-loud-but-nonfatal.patch ### Dependencies ### -Requires: krb5-libs >= 1.12.0 +Requires: krb5-libs >= 1.15 Requires: keyutils-libs -Requires: libverto-tevent +Requires: libverto-module-base Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units @@ -42,7 +38,7 @@ BuildRequires: doxygen BuildRequires: findutils BuildRequires: gettext-devel BuildRequires: keyutils-libs-devel -BuildRequires: krb5-devel >= 1.12.0 +BuildRequires: krb5-devel >= 1.15 BuildRequires: libini_config-devel >= 1.0.0.1 BuildRequires: libselinux-devel BuildRequires: libtool @@ -62,15 +58,10 @@ A proxy for GSSAPI credential handling %prep %setup -q - -%patch1 -p2 -b .gssproxy_ticket_140_0001-bug-140-remove-fno-strict-aliasing -%patch3 -p2 -b .gssproxy_ticket_145_130-set-default-rcache -%patch4 -p2 -b .gssproxy_ticket_143_workaround_service-http -%patch5 -p2 -b .gssproxy_EINTR_loop_fix -%patch6 -p2 -b .inquire_context_no_name -%patch7 -p2 -b .inquire_attrs_accept_null -%patch8 -p2 -b .socket_permission_checking -%patch9 -p2 -b .gssproxy_ticket_155-krb5_principal.patch +%patch0 -p2 -b .Properly-renew-expired-credentials +%patch1 -p2 -b .Change-impersonator-check-code +%patch2 -p2 -b .Allow-connection-to-self-when-impersonator-set +%patch3 -p2 -b .Make-proc-file-failure-loud-but-nonfatal %build autoreconf -f -i @@ -91,6 +82,7 @@ make install DESTDIR=%{buildroot} rm -f -- %{buildroot}%{_libdir}/gssproxy/proxymech.la install -d -m755 %{buildroot}%{_sysconfdir}/gssproxy install -m644 examples/gssproxy.conf %{buildroot}%{_sysconfdir}/gssproxy/gssproxy.conf +install -m644 examples/99-nfs-client.conf %{buildroot}%{_sysconfdir}/gssproxy/99-nfs-client.conf mkdir -p %{buildroot}%{_sysconfdir}/gss/mech.d install -m644 examples/mech %{buildroot}%{_sysconfdir}/gss/mech.d/gssproxy.conf mkdir -p %{buildroot}/var/lib/gssproxy/rcache @@ -108,6 +100,7 @@ rm -rf -- "%{buildroot}" %attr(755,root,root) %dir %{gpstatedir} %attr(700,root,root) %dir %{gpstatedir}/clients %attr(0600,root,root) %config(noreplace) /%{_sysconfdir}/gssproxy/gssproxy.conf +%attr(0600,root,root) %config(noreplace) /%{_sysconfdir}/gssproxy/99-nfs-client.conf %attr(0644,root,root) %config(noreplace) /%{_sysconfdir}/gss/mech.d/gssproxy.conf %attr(700,root,root) %dir /var/lib/gssproxy/rcache %{_libdir}/gssproxy/proxymech.so @@ -129,6 +122,47 @@ rm -rf -- "%{buildroot}" %changelog +* Wed May 31 2017 Robbie Harwood 0.7.0-4 +- Make proc file failure loud but nonfatal +- Resolves: #1449238 + +* Tue Mar 28 2017 Robbie Harwood 0.7.0-3 +- Stop shipping NFS server snippet (nfs-utils takes it instead) +- Resolves: #1379836 + +* Tue Mar 14 2017 Robbie Harwood 0.7.0-2 +- Fix credential handling with mod_auth_gssapi that we broke +- Resolves: #1379836 + +* Fri Mar 10 2017 Robbie Harwood 0.7.0-1 +- New upstream version - 0.7.0 +- Resolves: #1379836 + +* Tue Feb 28 2017 Robbie Harwood 0.6.2-4 +- Include fixes for NULL-termination +- Resolves: #1379836 + +* Thu Feb 23 2017 Robbie Harwood 0.6.2-3 +- Document debug_level option +- Resolves: #1379836 + +* Tue Feb 21 2017 Robbie Harwood 0.6.2-2 +- Enable running the test suite +- Resolves: #1379836 + +* Thu Feb 16 2017 Robbie Harwood 0.6.2-1 +- Rebase to latest version since we have krb5-1.15 +- Resolves: #1379836 +- Resolves: #1344518 +- Resolves: #1366782 +- Resolves: #1379005 +- Resolves: #1379482 +- Resolves: #1379616 +- Resolves: #1380490 +- Resolves: #1378600 +- Resolves: #1285012 +- Resolves: #1333813 + * Tue Sep 06 2016 Robbie Harwood 0.4.1-13 - Third try is the charm - Resolves: #1092515