Recording this issue in the bug database. Implementation is in progress. ==================== To: krbdev@mit.edu Date: Sat, 28 Jul 2007 02:30:28 -0400 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 krb5_walk_realm_tree(). 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. Notes: 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. Terminology: referrals: the service principal referral process described in draft-ietf-krb-wg-kerberos-referrals-09 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 realm. * "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 do_traversal().