krb5 commit: Expand dns_canonicalize_host=fallback support
Greg Hudson
ghudson at mit.edu
Fri Aug 7 18:49:00 EDT 2020
https://github.com/krb5/krb5/commit/3fcc365a6f049730b3f47168f7112c03997c5c0b
commit 3fcc365a6f049730b3f47168f7112c03997c5c0b
Author: Greg Hudson <ghudson at mit.edu>
Date: Fri Jul 17 22:57:45 2020 -0400
Expand dns_canonicalize_host=fallback support
In krb5_sname_to_principal(), when using fallback, defer realm lookup
and any kind of hostname canonicalization until use. Add a
lightweight iterator k5_canonprinc() to yield the one or two possible
candidates for a principal. In the iterator, don't yield the same
hostname part twice.
Add fallback processing to the stepwise TGS state machine, and remove
it from krb5_get_credentials(). Add fallback processing to
k5_get_proxy_cred_from_kdc().
Add fallback processing to krb5_init_creds_set_keytab(), and use the
principal we find in the keytab as the request client principal.
Defer restart_init_creds_loop() to the first step call so that server
principal is built using the correct realm.
Add fallback processing to krb5_rd_req().
ticket: 8930 (new)
src/include/k5-trace.h | 4 +-
src/kprop/kprop_util.c | 26 ++---
src/lib/krb5/krb/deps | 41 ++++----
src/lib/krb5/krb/get_creds.c | 151 ++++++++++++---------------
src/lib/krb5/krb/get_in_tkt.c | 7 +-
src/lib/krb5/krb/gic_keytab.c | 29 +++++-
src/lib/krb5/krb/init_creds_ctx.h | 1 +
src/lib/krb5/krb/rd_req_dec.c | 36 ++++++-
src/lib/krb5/krb/s4u_creds.c | 62 ++++++++---
src/lib/krb5/os/os-proto.h | 30 +++++
src/lib/krb5/os/sn2princ.c | 213 +++++++++++++++++++++++++-----------
src/tests/icred.c | 39 +++++--
src/tests/t_sn2princ.py | 55 +++++++---
13 files changed, 459 insertions(+), 235 deletions(-)
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 853a367..79ed740 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -229,8 +229,8 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
salt, s2kparams)
#define TRACE_INIT_CREDS_IDENTIFIED_REALM(c, realm) \
TRACE(c, "Identified realm of client principal as {data}", realm)
-#define TRACE_INIT_CREDS_KEYTAB_LOOKUP(c, etypes) \
- TRACE(c, "Looked up etypes in keytab: {etypes}", etypes)
+#define TRACE_INIT_CREDS_KEYTAB_LOOKUP(c, princ, etypes) \
+ TRACE(c, "Found entries for {princ} in keytab: {etypes}", princ, etypes)
#define TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(c, code) \
TRACE(c, "Couldn't lookup etypes in keytab: {kerr}", code)
#define TRACE_INIT_CREDS_PREAUTH(c) \
diff --git a/src/kprop/kprop_util.c b/src/kprop/kprop_util.c
index c32d174..c2b2e87 100644
--- a/src/kprop/kprop_util.c
+++ b/src/kprop/kprop_util.c
@@ -73,26 +73,22 @@ sn2princ_realm(krb5_context context, const char *hostname, const char *sname,
const char *realm, krb5_principal *princ_out)
{
krb5_error_code ret;
- char *canonhost, localname[MAXHOSTNAMELEN];
+ krb5_principal princ;
*princ_out = NULL;
assert(sname != NULL && realm != NULL);
- /* If hostname is NULL, use the local hostname. */
- if (hostname == NULL) {
- if (gethostname(localname, MAXHOSTNAMELEN) != 0)
- return SOCKET_ERRNO;
- hostname = localname;
- }
-
- ret = krb5_expand_hostname(context, hostname, &canonhost);
+ ret = krb5_sname_to_principal(context, hostname, sname, KRB5_NT_SRV_HST,
+ &princ);
if (ret)
return ret;
- ret = krb5_build_principal(context, princ_out, strlen(realm), realm, sname,
- canonhost, (char *)NULL);
- krb5_free_string(context, canonhost);
- if (!ret)
- (*princ_out)->type = KRB5_NT_SRV_HST;
- return ret;
+ ret = krb5_set_principal_realm(context, princ, realm);
+ if (ret) {
+ krb5_free_principal(context, princ);
+ return ret;
+ }
+
+ *princ_out = princ;
+ return 0;
}
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index 439ca02..6ac68bc 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -499,12 +499,13 @@ get_in_tkt.so get_in_tkt.po $(OUTPRE)get_in_tkt.$(OBJEXT): \
gic_keytab.so gic_keytab.po $(OUTPRE)gic_keytab.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
- $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
$(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
$(top_srcdir)/include/socket-utils.h gic_keytab.c init_creds_ctx.h \
int-proto.h
@@ -940,13 +941,14 @@ rd_req.so rd_req.po $(OUTPRE)rd_req.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
rd_req_dec.so rd_req_dec.po $(OUTPRE)rd_req_dec.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
- $(COM_ERR_DEPS) $(srcdir)/../rcache/memrcache.h $(top_srcdir)/include/k5-buf.h \
- $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/k5-utf8.h $(top_srcdir)/include/krb5.h \
- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(srcdir)/../rcache/memrcache.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/k5-utf8.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
auth_con.h authdata.h int-proto.h rd_req_dec.c
rd_safe.so rd_safe.po $(OUTPRE)rd_safe.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
@@ -997,12 +999,13 @@ s4u_authdata.so s4u_authdata.po $(OUTPRE)s4u_authdata.$(OBJEXT): \
s4u_creds.so s4u_creds.po $(OUTPRE)s4u_creds.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
- $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
- $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
- $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
int-proto.h s4u_creds.c
sendauth.so sendauth.po $(OUTPRE)sendauth.$(OBJEXT): \
diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index e0a3b5c..dc0aef6 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -119,7 +119,7 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
* generate the next request. If it's time to advance to another state, any of
* the three functions can make a tail call to begin_<nextstate> to do so.
*
- * The overall process is as follows:
+ * The general process is as follows:
* 1. Get a TGT for the service principal's realm (STATE_GET_TGT).
* 2. Make one or more referrals queries (STATE_REFERRALS).
* 3. In some cases, get a TGT for the fallback realm (STATE_GET_TGT again).
@@ -129,6 +129,9 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
* getting_tgt_for field in the context keeps track of what state we will go to
* after successfully obtaining the TGT, and the end_get_tgt() function
* advances to the proper next state.
+ *
+ * If fallback DNS canonicalization is in use, the process can be repeated a
+ * second time for the second server principal canonicalization candidate.
*/
enum state {
@@ -153,6 +156,8 @@ struct _krb5_tkt_creds_context {
krb5_flags req_options; /* Caller-requested KRB5_GC_* options */
krb5_flags req_kdcopt; /* Caller-requested options as KDC options */
krb5_authdata **authdata; /* Caller-requested authdata */
+ struct canonprinc iter; /* Iterator over canonicalized server princs */
+ krb5_boolean referral_req; /* Server initially contained referral realm */
/* The following fields are used in multiple steps. */
krb5_creds *cur_tgt; /* TGT to be used for next query */
@@ -484,7 +489,7 @@ try_fallback(krb5_context context, krb5_tkt_creds_context ctx)
/* If the request used a specified realm, make a non-referral request to
* that realm (in case it's a KDC which rejects KDC_OPT_CANONICALIZE). */
- if (!krb5_is_referral_realm(&ctx->req_server->realm))
+ if (!ctx->referral_req)
return begin_non_referral(context, ctx);
if (ctx->server->length < 2) {
@@ -1015,10 +1020,13 @@ check_cache(krb5_context context, krb5_tkt_creds_context ctx)
krb5_error_code code;
krb5_creds mcreds;
krb5_flags fields;
+ krb5_creds req_in_creds;
- /* Perform the cache lookup. */
+ /* Check the cache for the originally requested server principal. */
+ req_in_creds = *ctx->in_creds;
+ req_in_creds.server = ctx->req_server;
code = krb5int_construct_matching_creds(context, ctx->req_options,
- ctx->in_creds, &mcreds, &fields);
+ &req_in_creds, &mcreds, &fields);
if (code)
return code;
code = cache_get(context, ctx->ccache, fields, &mcreds, &ctx->reply_creds);
@@ -1044,12 +1052,9 @@ begin(krb5_context context, krb5_tkt_creds_context ctx)
{
krb5_error_code code;
- code = check_cache(context, ctx);
- if (code != 0 || ctx->state == STATE_COMPLETE)
- return code;
-
/* If the server realm is unspecified, start with the client realm. */
- if (krb5_is_referral_realm(&ctx->server->realm)) {
+ ctx->referral_req = krb5_is_referral_realm(&ctx->server->realm);
+ if (ctx->referral_req) {
krb5_free_data_contents(context, &ctx->server->realm);
code = krb5int_copy_data_contents(context, &ctx->client->realm,
&ctx->server->realm);
@@ -1072,6 +1077,7 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
{
krb5_error_code code;
krb5_tkt_creds_context ctx = NULL;
+ krb5_const_principal canonprinc;
TRACE_TKT_CREDS(context, in_creds, ccache);
ctx = k5alloc(sizeof(*ctx), &code);
@@ -1089,14 +1095,28 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
ctx->state = STATE_BEGIN;
+ /* Copy the matching cred so we can modify it. Steal the copy of the
+ * service principal name to remember the original request server. */
code = krb5_copy_creds(context, in_creds, &ctx->in_creds);
if (code != 0)
goto cleanup;
- ctx->client = ctx->in_creds->client;
- ctx->server = ctx->in_creds->server;
- code = krb5_copy_principal(context, ctx->server, &ctx->req_server);
+ ctx->req_server = ctx->in_creds->server;
+ ctx->in_creds->server = NULL;
+
+ /* Get the first canonicalization candidate for the requested server. */
+ ctx->iter.princ = ctx->req_server;
+
+ code = k5_canonprinc(context, &ctx->iter, &canonprinc);
+ if (code == 0 && canonprinc == NULL)
+ code = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
if (code != 0)
goto cleanup;
+ code = krb5_copy_principal(context, canonprinc, &ctx->in_creds->server);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->client = ctx->in_creds->client;
+ ctx->server = ctx->in_creds->server;
code = krb5_cc_dup(context, ccache, &ctx->ccache);
if (code != 0)
goto cleanup;
@@ -1138,6 +1158,7 @@ krb5_tkt_creds_free(krb5_context context, krb5_tkt_creds_context ctx)
return;
krb5int_fast_free_state(context, ctx->fast_state);
krb5_free_creds(context, ctx->in_creds);
+ free_canonprinc(&ctx->iter);
krb5_cc_close(context, ctx->ccache);
krb5_free_principal(context, ctx->req_server);
krb5_free_authdata(context, ctx->authdata);
@@ -1195,6 +1216,7 @@ krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context ctx,
{
krb5_error_code code;
krb5_boolean no_input = (in == NULL || in->length == 0);
+ krb5_const_principal canonprinc;
*out = empty_data();
*realm = empty_data();
@@ -1206,6 +1228,12 @@ krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context ctx,
ctx->state == STATE_COMPLETE)
return EINVAL;
+ if (ctx->state == STATE_BEGIN) {
+ code = check_cache(context, ctx);
+ if (code != 0 || ctx->state == STATE_COMPLETE)
+ return code;
+ }
+
ctx->caller_out = out;
ctx->caller_realm = realm;
ctx->caller_flags = flags;
@@ -1218,37 +1246,32 @@ krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context ctx,
}
if (ctx->state == STATE_BEGIN)
- return begin(context, ctx);
+ code = begin(context, ctx);
else if (ctx->state == STATE_GET_TGT)
- return step_get_tgt(context, ctx);
+ code = step_get_tgt(context, ctx);
else if (ctx->state == STATE_GET_TGT_OFFPATH)
- return step_get_tgt_offpath(context, ctx);
+ code = step_get_tgt_offpath(context, ctx);
else if (ctx->state == STATE_REFERRALS)
- return step_referrals(context, ctx);
+ code = step_referrals(context, ctx);
else if (ctx->state == STATE_NON_REFERRAL)
- return step_non_referral(context, ctx);
+ code = step_non_referral(context, ctx);
else
- return EINVAL;
-}
+ code = EINVAL;
-static krb5_error_code
-try_get_creds(krb5_context context, krb5_flags options, krb5_ccache ccache,
- krb5_creds *in_creds, krb5_creds *creds_out)
-{
- krb5_error_code code;
- krb5_tkt_creds_context ctx = NULL;
+ /* Terminate on success or most errors. */
+ if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
+ return code;
- code = krb5_tkt_creds_init(context, ccache, in_creds, options, &ctx);
+ /* Restart with the next server principal canonicalization candidate. */
+ code = k5_canonprinc(context, &ctx->iter, &canonprinc);
if (code)
- goto cleanup;
- code = krb5_tkt_creds_get(context, ctx);
- if (code)
- goto cleanup;
- code = krb5_tkt_creds_get_creds(context, ctx, creds_out);
-
-cleanup:
- krb5_tkt_creds_free(context, ctx);
- return code;
+ return code;
+ if (canonprinc == NULL)
+ return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ krb5_free_principal(context, ctx->in_creds->server);
+ code = krb5_copy_principal(context, canonprinc, &ctx->in_creds->server);
+ ctx->server = ctx->in_creds->server;
+ return begin(context, ctx);
}
krb5_error_code KRB5_CALLCONV
@@ -1258,10 +1281,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
{
krb5_error_code code;
krb5_creds *ncreds = NULL;
- krb5_creds canon_creds, store_creds;
- krb5_principal_data canon_server;
- krb5_data canon_components[2];
- char *hostname = NULL, *canon_hostname = NULL;
+ krb5_tkt_creds_context ctx = NULL;
*out_creds = NULL;
@@ -1277,59 +1297,22 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
if (ncreds == NULL)
goto cleanup;
- code = try_get_creds(context, options, ccache, in_creds, ncreds);
- if (!code) {
- *out_creds = ncreds;
- return 0;
- }
-
- /* Possibly try again with the canonicalized hostname, if the server is
- * host-based and we are configured for fallback canonicalization. */
- if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
- goto cleanup;
- if (context->dns_canonicalize_hostname != CANONHOST_FALLBACK)
- goto cleanup;
- if (in_creds->server->type != KRB5_NT_SRV_HST ||
- in_creds->server->length != 2)
- goto cleanup;
-
- hostname = k5memdup0(in_creds->server->data[1].data,
- in_creds->server->data[1].length, &code);
- if (hostname == NULL)
+ /* Make and execute a krb5_tkt_creds context to get the credential. */
+ code = krb5_tkt_creds_init(context, ccache, in_creds, options, &ctx);
+ if (code != 0)
goto cleanup;
- code = k5_expand_hostname(context, hostname, TRUE, &canon_hostname);
- if (code)
+ code = krb5_tkt_creds_get(context, ctx);
+ if (code != 0)
goto cleanup;
-
- TRACE_GET_CREDS_FALLBACK(context, canon_hostname);
-
- /* Make shallow copies of in_creds and its server to alter the hostname. */
- canon_components[0] = in_creds->server->data[0];
- canon_components[1] = string2data(canon_hostname);
- canon_server = *in_creds->server;
- canon_server.data = canon_components;
- canon_creds = *in_creds;
- canon_creds.server = &canon_server;
-
- code = try_get_creds(context, options | KRB5_GC_NO_STORE, ccache,
- &canon_creds, ncreds);
- if (code)
+ code = krb5_tkt_creds_get_creds(context, ctx, ncreds);
+ if (code != 0)
goto cleanup;
- if (!(options & KRB5_GC_NO_STORE)) {
- /* Store the creds under the originally requested server name. The
- * ccache layer will also store them under the ticket server name. */
- store_creds = *ncreds;
- store_creds.server = in_creds->server;
- (void)krb5_cc_store_cred(context, ccache, &store_creds);
- }
-
*out_creds = ncreds;
ncreds = NULL;
cleanup:
- free(hostname);
- free(canon_hostname);
krb5_free_creds(context, ncreds);
+ krb5_tkt_creds_free(context, ctx);
return code;
}
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index a1c4a53..feab6fb 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1051,9 +1051,6 @@ krb5_init_creds_init(krb5_context context,
ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
ctx->request->client->type = KRB5_NT_WELLKNOWN;
}
- code = restart_init_creds_loop(context, ctx, FALSE);
- if (code)
- goto cleanup;
*pctx = ctx;
ctx = NULL;
@@ -1859,6 +1856,10 @@ krb5_init_creds_step(krb5_context context,
}
if (code != 0 || ctx->complete)
goto cleanup;
+ } else {
+ code = restart_init_creds_loop(context, ctx, FALSE);
+ if (code)
+ goto cleanup;
}
code = init_creds_step_request(context, ctx, out);
diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c
index 1d70cf4..b2b4ac9 100644
--- a/src/lib/krb5/krb/gic_keytab.c
+++ b/src/lib/krb5/krb/gic_keytab.c
@@ -27,6 +27,7 @@
#include "k5-int.h"
#include "int-proto.h"
+#include "os-proto.h"
#include "init_creds_ctx.h"
static krb5_error_code
@@ -85,7 +86,8 @@ get_as_key_keytab(krb5_context context,
/* Return the list of etypes available for client in keytab. */
static krb5_error_code
lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
- krb5_principal client, krb5_enctype **etypes_out)
+ krb5_const_principal client,
+ krb5_enctype **etypes_out)
{
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
@@ -182,18 +184,37 @@ krb5_init_creds_set_keytab(krb5_context context,
{
krb5_enctype *etype_list;
krb5_error_code ret;
+ struct canonprinc iter = { ctx->request->client, .subst_defrealm = TRUE };
+ krb5_const_principal canonprinc;
+ krb5_principal copy;
char *name;
ctx->gak_fct = get_as_key_keytab;
ctx->gak_data = keytab;
- ret = lookup_etypes_for_keytab(context, keytab, ctx->request->client,
- &etype_list);
+ /* We may be authenticating as a host-based principal. If so, look for
+ * each canonicalization candidate in the keytab. */
+ while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
+ canonprinc != NULL) {
+ ret = lookup_etypes_for_keytab(context, keytab, canonprinc,
+ &etype_list);
+ if (ret || etype_list != NULL)
+ break;
+ }
+ if (!ret && canonprinc != NULL) {
+ /* Authenticate as the principal we found in the keytab. */
+ ret = krb5_copy_principal(context, canonprinc, ©);
+ if (!ret) {
+ krb5_free_principal(context, ctx->request->client);
+ ctx->request->client = copy;
+ }
+ }
+ free_canonprinc(&iter);
if (ret) {
TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(context, ret);
return 0;
}
- TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, etype_list);
+ TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, ctx->request->client, etype_list);
/* Error out if we have no keys for the client principal. */
if (etype_list == NULL) {
diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
index 5bd67a1..17d55dd 100644
--- a/src/lib/krb5/krb/init_creds_ctx.h
+++ b/src/lib/krb5/krb/init_creds_ctx.h
@@ -22,6 +22,7 @@ struct _krb5_init_creds_context {
krb5_get_init_creds_opt opt_storage;
krb5_boolean identify_realm;
const krb5_data *subject_cert;
+ krb5_principal keytab_princ;
char *in_tkt_service;
krb5_prompter_fct prompter;
void *prompter_data;
diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
index bc7fac4..013ca90 100644
--- a/src/lib/krb5/krb/rd_req_dec.c
+++ b/src/lib/krb5/krb/rd_req_dec.c
@@ -33,6 +33,7 @@
#include "auth_con.h"
#include "authdata.h"
#include "int-proto.h"
+#include "os-proto.h"
/*
* essentially the same as krb_rd_req, but uses a decoded AP_REQ as
@@ -351,9 +352,9 @@ try_one_princ(krb5_context context, const krb5_ap_req *req,
* Store the decrypting key in *keyblock_out if it is not NULL.
*/
static krb5_error_code
-decrypt_ticket(krb5_context context, const krb5_ap_req *req,
- krb5_const_principal server, krb5_keytab keytab,
- krb5_keyblock *keyblock_out)
+decrypt_try_server(krb5_context context, const krb5_ap_req *req,
+ krb5_const_principal server, krb5_keytab keytab,
+ krb5_keyblock *keyblock_out)
{
krb5_error_code ret;
krb5_keytab_entry ent;
@@ -442,6 +443,35 @@ decrypt_ticket(krb5_context context, const krb5_ap_req *req,
}
static krb5_error_code
+decrypt_ticket(krb5_context context, const krb5_ap_req *req,
+ krb5_const_principal server, krb5_keytab keytab,
+ krb5_keyblock *keyblock_out)
+{
+ krb5_error_code ret, dret = 0;
+ struct canonprinc iter = { server, .no_hostrealm = TRUE };
+ krb5_const_principal canonprinc;
+
+ /* Don't try to canonicalize if we're going to ignore the hostname, or if
+ * server is null or has a wildcard hostname. */
+ if (context->ignore_acceptor_hostname || server == NULL ||
+ (server->length == 2 && server->data[1].length == 0))
+ return decrypt_try_server(context, req, server, keytab, keyblock_out);
+
+ /* Try each canonicalization candidate for server. If they all fail,
+ * return the error from the last attempt. */
+ while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
+ canonprinc != NULL) {
+ dret = decrypt_try_server(context, req, canonprinc, keytab,
+ keyblock_out);
+ /* Only continue if we found no keytab entries matching canonprinc. */
+ if (dret != KRB5KRB_AP_ERR_NOKEY)
+ break;
+ }
+ free_canonprinc(&iter);
+ return (ret != 0) ? ret : dret;
+}
+
+static krb5_error_code
rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
const krb5_ap_req *req, krb5_const_principal server,
krb5_keytab keytab, krb5_flags *ap_req_options,
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index 5c2b6ff..2f12a17 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -26,6 +26,7 @@
#include "k5-int.h"
#include "int-proto.h"
+#include "os-proto.h"
/* Convert ticket flags to necessary KDC options */
#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
@@ -981,10 +982,10 @@ get_target_realm_proxy_tgt(krb5_context context, const krb5_data *realm,
return 0;
}
-krb5_error_code
-k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
- krb5_ccache ccache, krb5_creds *in_creds,
- krb5_creds **out_creds)
+static krb5_error_code
+get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
+ krb5_ccache ccache, krb5_creds *in_creds,
+ krb5_creds **out_creds)
{
krb5_error_code code;
krb5_flags flags, req_kdcopt = 0;
@@ -1120,22 +1121,11 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
}
}
- if (!krb5_principal_compare(context, in_creds->server, tkt->server)) {
- krb5_free_principal(context, tkt->server);
- tkt->server = NULL;
- code = krb5_copy_principal(context, in_creds->server, &tkt->server);
- if (code)
- goto cleanup;
- }
-
/* Note the authdata we asked for in the output creds. */
code = krb5_copy_authdata(context, in_creds->authdata, &tkt->authdata);
if (code)
goto cleanup;
- if (!(options & KRB5_GC_NO_STORE))
- (void)krb5_cc_store_cred(context, ccache, tkt);
-
*out_creds = tkt;
tkt = NULL;
@@ -1148,6 +1138,48 @@ cleanup:
return code;
}
+krb5_error_code
+k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
+ krb5_ccache ccache, krb5_creds *in_creds,
+ krb5_creds **out_creds)
+{
+ krb5_error_code code;
+ krb5_const_principal canonprinc;
+ krb5_creds copy, *creds;
+ struct canonprinc iter = { in_creds->server, .no_hostrealm = TRUE };
+
+ *out_creds = NULL;
+
+ copy = *in_creds;
+ while ((code = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
+ canonprinc != NULL) {
+ copy.server = (krb5_principal)canonprinc;
+ code = get_proxy_cred_from_kdc(context, options, ccache, ©,
+ &creds);
+ if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
+ break;
+ }
+ if (!code && canonprinc == NULL)
+ code = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ free_canonprinc(&iter);
+ if (code)
+ return code;
+
+ krb5_free_principal(context, creds->server);
+ creds->server = NULL;
+ code = krb5_copy_principal(context, in_creds->server, &creds->server);
+ if (code) {
+ krb5_free_creds(context, creds);
+ return code;
+ }
+
+ if (!(options & KRB5_GC_NO_STORE))
+ (void)krb5_cc_store_cred(context, ccache, creds);
+
+ *out_creds = creds;
+ return 0;
+}
+
/*
* Exported API for constrained delegation (S4U2Proxy).
*
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index a16a34b..f1aa60a 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -83,6 +83,36 @@ struct sendto_callback_info {
void *data;
};
+/*
+ * Initialize with all zeros except for princ. Set no_hostrealm to disable
+ * host-to-realm lookup, which ordinarily happens after canonicalizing the host
+ * part. Set subst_defrealm to substitute the default realm for the referral
+ * realm after realm lookup (this has no effect if no_hostrealm is set). Free
+ * with free_canonprinc() when done.
+ */
+struct canonprinc {
+ krb5_const_principal princ;
+ krb5_boolean no_hostrealm;
+ krb5_boolean subst_defrealm;
+ int step;
+ char *canonhost;
+ char *realm;
+ krb5_principal_data copy;
+ krb5_data components[2];
+};
+
+/* Yield one or two candidate canonical principal names for iter, then NULL.
+ * Output names are valid for one iteration and must not be freed. */
+krb5_error_code k5_canonprinc(krb5_context context, struct canonprinc *iter,
+ krb5_const_principal *princ_out);
+
+static inline void
+free_canonprinc(struct canonprinc *iter)
+{
+ free(iter->canonhost);
+ free(iter->realm);
+}
+
krb5_error_code k5_expand_hostname(krb5_context context, const char *host,
krb5_boolean is_fallback,
char **canonhost_out);
diff --git a/src/lib/krb5/os/sn2princ.c b/src/lib/krb5/os/sn2princ.c
index a51761d..8b72141 100644
--- a/src/lib/krb5/os/sn2princ.c
+++ b/src/lib/krb5/os/sn2princ.c
@@ -85,22 +85,18 @@ qualify_shortname(krb5_context context, const char *host)
return fqdn;
}
-krb5_error_code
-k5_expand_hostname(krb5_context context, const char *host,
- krb5_boolean is_fallback, char **canonhost_out)
+static krb5_error_code
+expand_hostname(krb5_context context, const char *host, krb5_boolean use_dns,
+ char **canonhost_out)
{
struct addrinfo *ai = NULL, hint;
char namebuf[NI_MAXHOST], *qualified = NULL, *copy, *p;
int err;
const char *canonhost;
- krb5_boolean use_dns;
*canonhost_out = NULL;
canonhost = host;
- use_dns = (context->dns_canonicalize_hostname == CANONHOST_TRUE ||
- (is_fallback &&
- context->dns_canonicalize_hostname == CANONHOST_FALLBACK));
if (use_dns) {
/* Try a forward lookup of the hostname. */
memset(&hint, 0, sizeof(hint));
@@ -161,21 +157,135 @@ krb5_error_code KRB5_CALLCONV
krb5_expand_hostname(krb5_context context, const char *host,
char **canonhost_out)
{
- return k5_expand_hostname(context, host, FALSE, canonhost_out);
+ int use_dns = (context->dns_canonicalize_hostname == CANONHOST_TRUE);
+
+ return expand_hostname(context, host, use_dns, canonhost_out);
+}
+
+/* Split data into hostname and trailer (:port or :instance). Trailers are
+ * used in MSSQLSvc principals. */
+static void
+split_trailer(const krb5_data *data, krb5_data *host, krb5_data *trailer)
+{
+ char *p = memchr(data->data, ':', data->length);
+ unsigned int tlen = (p == NULL) ? 0 : data->length - (p - data->data);
+
+ /* Make sure we have a single colon followed by one or more characters. An
+ * IPv6 address will have more than one colon, so don't accept that. */
+ if (p == NULL || tlen == 1 || memchr(p + 1, ':', tlen - 1) != NULL) {
+ *host = *data;
+ *trailer = empty_data();
+ } else {
+ *host = make_data(data->data, p - data->data);
+ *trailer = make_data(p, tlen);
+ }
}
-/* If hostname appears to have a :port or :instance trailer (used in MSSQLSvc
- * principals), return a pointer to the separator. Otherwise return NULL. */
-static const char *
-find_trailer(const char *hostname)
+static krb5_error_code
+canonicalize_princ(krb5_context context, struct canonprinc *iter,
+ krb5_boolean use_dns, krb5_const_principal *princ_out)
{
- const char *p = strchr(hostname, ':');
+ krb5_error_code ret;
+ krb5_data host, trailer;
+ char *hostname = NULL, *canonhost = NULL, *combined = NULL;
+ char **hrealms = NULL;
- /* Look for a single colon followed by one or more characters. An IPv6
- * address will have more than one colon, so don't accept that. */
- if (p == NULL || p[1] == '\0' || strchr(p + 1, ':') != NULL)
- return NULL;
- return p;
+ *princ_out = NULL;
+
+ assert(iter->princ->length == 2);
+ split_trailer(&iter->princ->data[1], &host, &trailer);
+
+ hostname = k5memdup0(host.data, host.length, &ret);
+ if (hostname == NULL)
+ goto cleanup;
+
+ if (iter->princ->type == KRB5_NT_SRV_HST) {
+ /* Expand the hostname with or without DNS as specified. */
+ ret = expand_hostname(context, hostname, use_dns, &canonhost);
+ if (ret)
+ goto cleanup;
+ } else {
+ canonhost = strdup(hostname);
+ if (canonhost == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ }
+
+ /* Add the trailer to the expanded hostname. */
+ if (asprintf(&combined, "%s%.*s", canonhost,
+ trailer.length, trailer.data) < 0) {
+ combined = NULL;
+ ret = ENOMEM;
+ goto cleanup;
+ }
+
+ /* Don't yield the same host part twice. */
+ if (iter->canonhost != NULL && strcmp(iter->canonhost, combined) == 0)
+ goto cleanup;
+
+ free(iter->canonhost);
+ iter->canonhost = combined;
+ combined = NULL;
+
+ /* If the realm is unknown, look up the realm of the expanded hostname. */
+ if (iter->princ->realm.length == 0 && !iter->no_hostrealm) {
+ ret = krb5_get_host_realm(context, canonhost, &hrealms);
+ if (ret)
+ goto cleanup;
+ if (hrealms[0] == NULL) {
+ ret = KRB5_ERR_HOST_REALM_UNKNOWN;
+ goto cleanup;
+ }
+ free(iter->realm);
+ if (*hrealms[0] == '\0' && iter->subst_defrealm) {
+ ret = krb5_get_default_realm(context, &iter->realm);
+ if (ret)
+ goto cleanup;
+ } else {
+ iter->realm = strdup(hrealms[0]);
+ if (iter->realm == NULL) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+ }
+ }
+
+ iter->copy = *iter->princ;
+ if (iter->realm != NULL)
+ iter->copy.realm = string2data(iter->realm);
+ iter->components[0] = iter->princ->data[0];
+ iter->components[1] = string2data(iter->canonhost);
+ iter->copy.data = iter->components;
+ *princ_out = &iter->copy;
+
+cleanup:
+ free(hostname);
+ free(canonhost);
+ free(combined);
+ krb5_free_host_realm(context, hrealms);
+ return ret;
+}
+
+krb5_error_code
+k5_canonprinc(krb5_context context, struct canonprinc *iter,
+ krb5_const_principal *princ_out)
+{
+ int step = ++iter->step;
+
+ *princ_out = NULL;
+
+ /* If we're not doing fallback, the input principal is canonical. */
+ if (context->dns_canonicalize_hostname != CANONHOST_FALLBACK ||
+ iter->princ->type != KRB5_NT_SRV_HST || iter->princ->length != 2) {
+ *princ_out = (step == 1) ? iter->princ : NULL;
+ return 0;
+ }
+
+ /* Canonicalize without DNS at step 1, with DNS at step 2. */
+ if (step > 2)
+ return 0;
+ return canonicalize_princ(context, iter, step == 2, princ_out);
}
krb5_error_code KRB5_CALLCONV
@@ -185,9 +295,10 @@ krb5_sname_to_principal(krb5_context context, const char *hostname,
{
krb5_error_code ret;
krb5_principal princ;
- const char *realm, *trailer;
- char **hrealms = NULL, *canonhost = NULL, *hostonly = NULL, *concat = NULL;
+ krb5_const_principal cprinc;
+ krb5_boolean use_dns;
char localname[MAXHOSTNAMELEN];
+ struct canonprinc iter = { NULL };
*princ_out = NULL;
@@ -205,54 +316,26 @@ krb5_sname_to_principal(krb5_context context, const char *hostname,
if (sname == NULL)
sname = "host";
- /* If there is a trailer, remove it for now. */
- trailer = find_trailer(hostname);
- if (trailer != NULL) {
- hostonly = k5memdup0(hostname, trailer - hostname, &ret);
- if (hostonly == NULL)
- goto cleanup;
- hostname = hostonly;
- }
-
- /* Canonicalize the hostname if appropriate. */
- if (type == KRB5_NT_SRV_HST) {
- ret = krb5_expand_hostname(context, hostname, &canonhost);
- if (ret)
- goto cleanup;
- hostname = canonhost;
- }
-
- /* Find the realm of the host. */
- ret = krb5_get_host_realm(context, hostname, &hrealms);
+ /* Build an initial principal with what we have. */
+ ret = krb5_build_principal(context, &princ, 0, KRB5_REFERRAL_REALM,
+ sname, hostname, (char *)NULL);
if (ret)
- goto cleanup;
- if (hrealms[0] == NULL) {
- ret = KRB5_ERR_HOST_REALM_UNKNOWN;
- goto cleanup;
- }
- realm = hrealms[0];
+ return ret;
+ princ->type = type;
- /* If there was a trailer, put it back on the end. */
- if (trailer != NULL) {
- if (asprintf(&concat, "%s%s", hostname, trailer) < 0) {
- ret = ENOMEM;
- goto cleanup;
- }
- hostname = concat;
+ if (type == KRB5_NT_SRV_HST &&
+ context->dns_canonicalize_hostname == CANONHOST_FALLBACK) {
+ /* Delay canonicalization and realm lookup until use. */
+ *princ_out = princ;
+ return 0;
}
- ret = krb5_build_principal(context, &princ, strlen(realm), realm, sname,
- hostname, (char *)NULL);
- if (ret)
- goto cleanup;
-
- princ->type = type;
- *princ_out = princ;
-
-cleanup:
- free(hostonly);
- free(canonhost);
- free(concat);
- krb5_free_host_realm(context, hrealms);
+ use_dns = (context->dns_canonicalize_hostname == CANONHOST_TRUE);
+ iter.princ = princ;
+ ret = canonicalize_princ(context, &iter, use_dns, &cprinc);
+ if (!ret)
+ ret = krb5_copy_principal(context, cprinc, princ_out);
+ free_canonprinc(&iter);
+ krb5_free_principal(context, princ);
return ret;
}
diff --git a/src/tests/icred.c b/src/tests/icred.c
index 55f929c..d6ce1d5 100644
--- a/src/tests/icred.c
+++ b/src/tests/icred.c
@@ -30,10 +30,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/*
- * This program exercises the init_creds APIs in ways kinit doesn't. Right now
- * it is very simplistic, but it can be extended as needed.
- */
+/* This program exercises the init_creds APIs in ways kinit doesn't. */
#include "k5-platform.h"
#include <krb5.h>
@@ -56,10 +53,11 @@ check(krb5_error_code code)
int
main(int argc, char **argv)
{
- const char *princstr, *password;
+ const char *ktname = NULL, *sname = NULL, *princstr, *password;
krb5_principal client;
krb5_init_creds_context icc;
krb5_get_init_creds_opt *opt;
+ krb5_keytab keytab = NULL;
krb5_creds creds;
krb5_boolean stepwise = FALSE;
krb5_preauthtype ptypes[64];
@@ -69,8 +67,11 @@ main(int argc, char **argv)
check(krb5_init_context(&ctx));
check(krb5_get_init_creds_opt_alloc(ctx, &opt));
- while ((c = getopt(argc, argv, "so:X:")) != -1) {
+ while ((c = getopt(argc, argv, "k:so:S:X:")) != -1) {
switch (c) {
+ case 'k':
+ ktname = optarg;
+ break;
case 's':
stepwise = TRUE;
break;
@@ -78,6 +79,9 @@ main(int argc, char **argv)
assert(nptypes < 64);
ptypes[nptypes++] = atoi(optarg);
break;
+ case 'S':
+ sname = optarg;
+ break;
case 'X':
val = strchr(optarg, '=');
if (val != NULL)
@@ -93,12 +97,20 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (argc != 2)
+ if (argc != 1 && argc != 2)
abort();
princstr = argv[0];
password = argv[1];
- check(krb5_parse_name(ctx, princstr, &client));
+ if (sname != NULL) {
+ check(krb5_sname_to_principal(ctx, princstr, sname, KRB5_NT_SRV_HST,
+ &client));
+ } else {
+ check(krb5_parse_name(ctx, princstr, &client));
+ }
+
+ if (ktname != NULL)
+ check(krb5_kt_resolve(ctx, ktname, &keytab));
if (nptypes > 0)
krb5_get_init_creds_opt_set_preauth_list(opt, ptypes, nptypes);
@@ -106,9 +118,16 @@ main(int argc, char **argv)
if (stepwise) {
/* Use the stepwise interface. */
check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, NULL, &icc));
- check(krb5_init_creds_set_password(ctx, icc, password));
+ if (keytab != NULL)
+ check(krb5_init_creds_set_keytab(ctx, icc, keytab));
+ if (password != NULL)
+ check(krb5_init_creds_set_password(ctx, icc, password));
check(krb5_init_creds_get(ctx, icc));
krb5_init_creds_free(ctx, icc);
+ } else if (keytab != NULL) {
+ check(krb5_get_init_creds_keytab(ctx, &creds, client, keytab, 0, NULL,
+ opt));
+ krb5_free_cred_contents(ctx, &creds);
} else {
/* Use the traditional one-shot interface. */
check(krb5_get_init_creds_password(ctx, &creds, client, password, NULL,
@@ -116,6 +135,8 @@ main(int argc, char **argv)
krb5_free_cred_contents(ctx, &creds);
}
+ if (keytab != NULL)
+ krb5_kt_close(ctx, keytab);
krb5_get_init_creds_opt_free(ctx, opt);
krb5_free_principal(ctx, client);
krb5_free_context(ctx);
diff --git a/src/tests/t_sn2princ.py b/src/tests/t_sn2princ.py
index ca94af4..0b63dbe 100755
--- a/src/tests/t_sn2princ.py
+++ b/src/tests/t_sn2princ.py
@@ -85,38 +85,61 @@ if offline:
oname = 'ptr-mismatch.kerberos.org'
fname = 'www.kerberos.org'
-# Test fallback canonicalization krb5_sname_to_principal() results
-# (same as dns_canonicalize_hostname=false).
+# Test fallback canonicalization krb5_sname_to_principal() results.
mark('dns_canonicalize_host=fallback')
-testfc(oname, oname, 'R1')
+testfc(oname, oname, '')
+
+# Verify forward resolution before testing for it.
+try:
+ ai = socket.getaddrinfo(oname, None, 0, 0, 0, socket.AI_CANONNAME)
+except socket.gaierror:
+ skip_rest('sn2princ tests', 'cannot forward resolve %s' % oname)
+(family, socktype, proto, canonname, sockaddr) = ai[0]
+if canonname.lower() != fname:
+ skip_rest('sn2princ tests',
+ '%s forward resolves to %s, not %s' % (oname, canonname, fname))
# Test fallback canonicalization in krb5_get_credentials().
oprinc = 'host/' + oname
fprinc = 'host/' + fname
shutil.copy(realm.ccache, realm.ccache + '.save')
+# Test that we only try fprinc once if we enter it as input.
+out, trace = realm.run(['./gcred', 'srv-hst', fprinc + '@'],
+ env=fallback_canon, expected_code=1, return_trace=True)
+msg = 'Requesting tickets for %s at R1, referrals on' % fprinc
+if trace.count(msg) != 1:
+ fail('Expected one try for %s' % fprinc)
+# Create fprinc, and verify that we get it as the canonicalized
+# fallback for oprinc.
realm.addprinc(fprinc)
-# oprinc doesn't exist, so we get the canonicalized fprinc as a fallback.
-msgs = ('Falling back to canonicalized server hostname ' + fname,)
-realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
+msgs = ('Getting credentials user at R1 -> %s@ using' % oprinc,
+ 'Requesting tickets for %s at R1' % oprinc,
+ 'Requesting tickets for %s at R1' % fprinc,
+ 'Received creds for desired service %s at R1' % fprinc)
+realm.run(['./gcred', 'srv-hst', oprinc + '@'], env=fallback_canon,
expected_msg=fprinc, expected_trace=msgs)
realm.addprinc(oprinc)
# oprinc now exists, but we still get the fprinc ticket from the cache.
-realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
+realm.run(['./gcred', 'srv-hst', oprinc + '@'], env=fallback_canon,
expected_msg=fprinc)
# Without the cached result, we should get oprinc in preference to fprinc.
os.rename(realm.ccache + '.save', realm.ccache)
realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
expected_msg=oprinc)
-# Verify forward resolution before testing for it.
-try:
- ai = socket.getaddrinfo(oname, None, 0, 0, 0, socket.AI_CANONNAME)
-except socket.gaierror:
- skip_rest('sn2princ tests', 'cannot forward resolve %s' % oname)
-(family, socktype, proto, canonname, sockaddr) = ai[0]
-if canonname.lower() != fname:
- skip_rest('sn2princ tests',
- '%s forward resolves to %s, not %s' % (oname, canonname, fname))
+# Test fallback canonicalization for krb5_rd_req().
+realm.run([kadminl, 'ktadd', fprinc])
+msgs = ('Decrypted AP-REQ with server principal %s at R1' % fprinc,
+ 'AP-REQ ticket: user at R1 -> %s at R1' % fprinc)
+realm.run(['./rdreq', fprinc, oprinc + '@'], env=fallback_canon,
+ expected_trace=msgs)
+
+# Test fallback canonicalization for getting initial creds with a keytab.
+msgs = ('Getting initial credentials for %s@' % oprinc,
+ 'Found entries for %s at R1 in keytab' % fprinc,
+ 'Retrieving %s at R1 from ' % fprinc)
+realm.run(['./icred', '-k', realm.keytab, '-S', 'host', oname],
+ env=fallback_canon, expected_trace=msgs)
# Test forward-only canonicalization (rdns=false).
mark('rdns=false')
More information about the cvs-krb5
mailing list