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