Skip Menu |
 

Subject: master_kdc is resolved sooner than necessary
When krb5_sendto_kdc gets a response, successful or not, it immediately
looks up the master_kdc value so it can set the value of *use_master. If
the response is a failure, the caller may use the returned value of
*use_master to avoid resending to the master KDC if we happened to pick
it the first time around.

But in some common cases, the returned value of *use_master is not used.
It would be more efficient if we looked up the master KDC only after
determining that the response is a failure that we want to fall back
from.

Combined with #6782, this issue can cause a DNS lookup to be performed
for every request, even ones with successful replies, for a realm which
has krb5.conf configuration for "kdc" but not "master_kdc".
Subject: primary_kdc is resolved sooner than necessary
Currently, the fallback to primary is accomplished by checking whether the primary was used in krb5_sendto_kdc(), communicating this back up the chain through k5_init_creds_get() and k5_get_init_creds() to krb5_get_init_creds_password().  If a failure is observed other than four specific codes (indicating a KDC connectivity or password entry issue), krb5_get_init_creds_password() retries the whole AS exchange with use_primary set.  If k5_init_creds_get() makes multiple requests; the use_primary value communicated up the chain is for the last request sent.

When evaluating alternative designs, it is important to consider whether the fallback must retry only the final request or (as it currently does) the entire exchange.  Retrying the final request would be sufficient for many multi-request exchanges; for example, if encrypted timestamp failed because of a recent password change, retrying the preauthenticated request would solve the problem.  If the account was recently created, retrying the final request might need to be iterated, as both the unauthenticated and preauthenticated requests would fail against an outdated replica KDC.  In more complicated scenarios we could see a failure due to an earlier request using a replica KDC.  Some examples include:

* When using SPAKE, if the SPAKEChallenge was sent by an replica KDC with an outdated long-term key, we would only see a failure after the SPAKEResponse is sent.

* If an AS request requires a WRONG_REALM referral and the referral target changes because the account is moved, we would only see a failure after the old realm is contacted.

Note that for these cases the current design can erroneously conclude that a fallback wouldn't be productive, because it only looks at the use_primary value for the final request.  This problem could be rectified easily enough by making k5_init_creds_get() communicate the logical AND of use_primary results to the caller.

Some candidate designs:

* krb5_sendto_kdc() could evaluate the KDC error code and retry against the primary KDC internally.  This would work for simple scenarios but not more complex ones.  This design requires somewhat expansive knowledge of error codes to know when not to retry--not just the ones indicating unrecoverable failures but ones such as PREAUTH_REQUIRED which are handled as part of multi-request exchanges.  (In theory the set of non-fallback error codes is unbounded due to preauth errors such as KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, but retrying on such errors might be an acceptable inefficiency.)

* krb5_sendto_kdc() could perform the lookup only when it's cheap (no use of DNS), leaving *use_primary as 0 otherwise.  We would retry in more failure cases but wouldn't make any extra DNS requests on success.  This design would require changes to k5_locate_kdc(), and would require deciding whether locate plugins are assumed to be cheap or expensive (until a refresh of the locate interface, which we want for other reasons).

* krb5_sendto_kdc() could communicate the server it used back to the caller (e.g. by transferring ownership of servers.servers[server_used]).  The caller could then query it with k5_kdc_is_primary().  Properly handling the complicated cases would require maintaining an array of {realm, server} objects and querying all of them until one is determined not to be primary.

* krb5_sendto_kdc() could have a state object which persists after the call.  We could then call krb5_sendto_kdc() again with the same state object, and it could determine whether its prior answer was from a primary KDC.  Properly handling the complicated cases would require maintaining an array of state objects.  This design might require implementing https://k5wiki.kerberos.org/wiki/Projects/Rule_based_sendto_kdc_loop .
 
The fourth candidate fails because we cannot (without major changes) rewind the initial creds state to the point of prior KDC requests.

The third candidate could be perhaps improved by having krb5_sendto_kdc() append to a history of servers used; the servers could then be checked in one function call making fewer DNS queries (one query per realm contacted during the exchange).   A side note: what we really want to check for is not "are all these servers primary" but "are any of these servers replicas".  If a realm does not have a primary KDC, we want to treat all of its KDCs like primary KDCs for the purpose of deciding whether to fall back.
 
From: ghudson@mit.edu
Subject: git commit
Download (untitled) / with headers
text/plain 1.6KiB

Defer primary KDC lookups

Add an internal variant of krb5_sendto_kdc() which records the
answering KDC in a list. Callers can check the list for replica KDC
use after the success or failure of the KDC exchange is determined,
avoiding DNS queries for the primary KDCs in many common cases and
using fewer DNS queries in other cases.

Perform the fallback in k5_get_init_creds() rather than
krb5_get_init_creds_password(). For now we must additionally perform
the fallback in krb5_get_init_creds_keytab() as it does not use
k5_get_init_creds().

Preserve the current signature of krb5_sendto_kdc() (it is used within
the tree outside of libkrb5, and might be used by other software
despite being non-public), but remove the behavior of setting
*use_primary.

https://github.com/krb5/krb5/commit/fabbf11f457a84904a5fa251584fd660a52fa583
Author: Greg Hudson <ghudson@mit.edu>
Commit: fabbf11f457a84904a5fa251584fd660a52fa583
Branch: master
src/include/k5-trace.h | 6 +-
src/lib/krb5/krb/gc_via_tkt.c | 12 +--
src/lib/krb5/krb/get_creds.c | 11 ++-
src/lib/krb5/krb/get_etype_info.c | 14 ++--
src/lib/krb5/krb/get_in_tkt.c | 87 ++++++++++++++++------
src/lib/krb5/krb/gic_keytab.c | 23 +++---
src/lib/krb5/krb/gic_pwd.c | 73 +++---------------
src/lib/krb5/krb/in_tkt_sky.c | 4 +-
src/lib/krb5/krb/int-proto.h | 8 +-
src/lib/krb5/os/locate_kdc.c | 152 +++++++++++++++++++++++++++++++++++---
src/lib/krb5/os/os-proto.h | 24 +++++-
src/lib/krb5/os/sendto_kdc.c | 46 ++++++------
src/tests/Makefile.in | 1 +
src/tests/t_sendto_kdc.py | 28 +++++++
14 files changed, 329 insertions(+), 160 deletions(-)