off-path TGT referrals in krb5_get_cred_from_kdc()

Tom Yu tlyu at MIT.EDU
Sat Jul 28 02:30:28 EDT 2007

I'm trying to design a solution to an existing problem with the
handling of cross-realm traversal in krb5_get_cred_from_kdc() when a
domain_realm mapping is present for the service principal.  This can
lead to "off-path" RFC 4120 style referral TGTs being returned, which
currently causes a hard failure.

Some possible options:

1. Chase the referral TGT chain, asking each successive KDC for the
   destination realm's TGT.  Do loop detection and hop-count limiting
   as in the "referrals path".  Only cache obtained TGTs obtained
   prior to the off-path referral and the destination realm TGT.

2. Do as in (1), except also ask a KDC for nearer TGTs if it doesn't
   know how to get to the destination realm.  This resembles existing
   do_traversal() logic for paths obtained from

3. Return from do_traversal() and ignore TGTs obtained so far, then
   use local TGT to run "referrals path".  This could be problematic
   if the local KDC doesn't support service principal
   canonicalization, so we probably won't do this.


I think we should do option (1).  An explicitly specified service
realm name probably shouldn't get rewritten except by that realm's
KDC.  That probably rules out option (3) above.  Also, network traffic
considerations probably rule out option (2).


Some further discussion and background is below.  It's also useful to
refer to RT ticket #2652, which contains additional design notes.


    referrals: the service principal referral process described in

    TGT referral: the limited "shortcut" referral process permitted in
        RFC 4120 when obtaining cross-realm TGTs

    destination realm: realm containing the destination service


Outline of existing krb5_get_cred_from_kdc() implementation:

* In service name, rewrite empty-string realm to local realm - usually
  happens when referrals are requested by krb5_sname_to_principal() in
  the absence of domain_realm mappings.

* Run do_traversal() to get TGTs leading to the (initial) destination

* "referrals code path"

  - With KDC_OPT_CANONICALIZE turned on, ask destination realm KDC for
    service ticket and chase referrals if given.  We currently do not
    support service name rewriting; only the service realm name gets
    rewritten if we get a TGT back.

  - If KDC returns an error when we turn on KDC_OPT_CANONICALIZE,
    retry without the flag, unless this is the first pass, in which
    case we just fall back to not doing canonicalization.  If the
    retry still gets an error, fail hard.

  - Exceeding max loop count or receiving a non-TGT principal which is
    not the requested service principal causes fallback.

* "fallback"

  - If referrals were originally requested, determine a fallback realm
    and use that.

  - Run do_traversal to get TGTs leading to destination realm.

* Use whatever final TGT to get the service ticket.

* Return TGTs for caller to cache; don't return intermediate referral
  TGTs, only the final one.  Return all TGTs from do_traversal()
  because we presumably trust those.

RFC 4120 allows for limited service principal referrals for TGT
principals without the client needing to set KDC_OPT_CANONICALIZE.  In
particular, if a client requests a cross-realm TGT for some
destination realm, and the KDC queried does not have a direct
cross-realm key with that destination realm, the KDC can return the
TGT for a "closer" realm in the transit path.

Problems arise when a client has a different idea of the transit path
than the realms along the transit path.  The realms along the transit
path may not even agree with each other about what the transit path
is.  Our current implementation only supports one linear realm transit
path at a time.  This causes failures when it receives an "off-path"
TGT referral from some KDC.

The krb5_walk_realm_tree() function returns either a hierarchical
traversal path produced by decomposing domain-style realm names, or a
path configured manually using capaths.  The internal function
do_traversal() attempts to ask the nearest realm for a cross-realm TGT
for the furthest realm.  If this fails, do_traversal() walks the
"furthest" pointer backwards and continues asking the nearest realm
for TGTs for closer realms until it gets one.  Whenever the client
successfully obtains a cross-realm TGT, it sets its idea of the
"nearest" realm to the target of that cross-realm TGT and starts
walking the "furthest" pointer backwards from the destination realm.

do_traversal() accepts "shortcut" referral TGTs that do not
necessarily reach the destination realm, but point farther along the
transit path.  The problem is that if the KDC providing the referral
TGT has a different idea of the transit path than the client,
do_traversal() fails hard.  In the case where a domain_realm mapping
gets set on the client, but the client has no corresponding capath to
the destination, this creates a situation where the "referrals" code
path would have found the path, but do_traversal() would not.

It is useful if the client can actually make use of these off-path
referral TGTs to obtain a TGT for the destination realm of interest.
Obviously some paths should be more trusted than others; paths
configured manually or derived from hierarchical traversal may be more
trusted than a path obtained by an off-path TGT referral.

In the "referrals" path, where the client does not have direct
knowledge of what realm the target service lives in, the client will
accept cross-realm TGTs that it would otherwise consider to be
"off-path".  Currently, our implementation makes an effort to only
cache the final-hop TGT, and none of the intermediate ones.  We
probably want to do a similar thing when we receive an off-path TGT in

More information about the krbdev mailing list