Projects/Initial creds improvements
This project is mostly internal design changes to the initial creds API. The immediate user-visible impact should not be significant.
Contents
Background
get_in_tkt.c implements the krb5_init_creds_step() API, which is responsible for processing a KDC reply and generating the next request (or terminating the exchange). Currently this function is divided into two helper functions: init_creds_step_reply(), which examines the KDC reply and alters the request state, and init_creds_step_request(), which generates a request based on the current state.
The current code has the following limitations, most of which do not arise in current pre-authentication scenarios:
- If two krb5_init_creds_step() operations are interleaved using the same krb5_context, they erroneously share per-request per-module data pointers, which may confuse clpreauth modules. (They also share the "tried" list which tracks previously tried mechanisms, but that list is not currently doing much.) [krbdev.mit.edu #7877] [now fixed]
- Support for optimistic preauth is very limited. It must be initiated by the calling application, not by configuration, and there is no way to specify salt or s2kparams for producing the reply key. We currently retry optimistic preauth after realm referrals and other conversation restarts, which may be unnecessarily complicated.
- If a preauth mechanism fails (even during optimistic preauth), we do not continue on to other mechanisms, unless it failed on the client side during method-data processing.
- If we receive a PREAUTH_FAILED error from the KDC with e-data containing METHOD-DATA (as is recommended for KDCs in RFC 6113), we invoke clpreauth module tryagain methods, which is fruitless and can obscure error messages.
- clpreauth modules do not get much information about the padata they are processing. It could be from optimistic preauth, from KDC METHOD-DATA in a PREAUTH_REQUIRED error, from KDC METHOD-DATA in a MORE_PREAUTH_DATA_REQUIRED error, or from an AS-REP. (Other KDC errors are handled via the tryagain method, so those are distinguishable.)
- clpreauth modules cannot indicate that they expect additional padata values before the mechanism is complete. In the middle of a conversation, a KDC could issue an AS-REP with no padata and the client would try to decrypt it with whatever the reply key currently is. This limitation has no current security consequences, but could become important in the future.
- The preferred_preauth_types realm variable is a hack; the order of KDC padata should be the preferred order. However, it is important to keep trying PKINIT first by default as KDCs do not always put it before encrypted timestamp when they ought to.
- krb5_get_init_creds_get_error() cannot retrieve most intermediate errors. [krbdev.mit.edu #8222]
Improvements
Proper scoping of per-request preauth data
The per-request krb5_clpreauth_modreq pointers for each module should be maintained in an object separate from krb5_preauth_context. This object should be linked from krb5_init_creds_context.
The "tried" list is currently unused and should be removed. A list of preauthentication mechanisms which have failed should be maintained in the per-request preauth state.
[This work is now done; see commit b061f419cfc9653b7549b905e54fbbd78deea49e.]
Optimistic preauth
TBD
Continuing after failures
A preauth mechanism can fail in several ways:
- The clpreauth module's process method can fail to generate initial padata value during optimistic preauth or processing of KDC method-data. In this case we already continue on to the next mechanism.
- The KDC can reply with KDC_ERR_PREAUTH_FAILED, with or without METHOD-DATA.
- The KDC can reply with KDC_ERR_MORE_PREAUTH_DATA_REQUIRED, and the process method can fail to generate a padata value.
- The KDC can reply with another error containing e-data, and the tryagain method can fail to generate a padata value.
If optimistic preauth results in KDC_ERR_PREAUTH_FAILED, we should discard per-request preauth state and begin anew. If the KDC included METHOD-DATA, we should use that; otherwise we should send an unauthenticated request in order to get a KDC_ERR_PREAUTH_REQUIRED error with METHOD-DATA. If optimistic preauth results in a different kind of failure, we should send an unauthenticated request.
The first time we receive a KDC METHOD-DATA list, we should remember it for the purpose of continuing after failures. We should discard this list on a KDC_ERR_PREAUTH_EXPIRED error (as the METHOD-DATA list may include a stale cookie). If we receive multiple METHOD-DATA lists, we can either replace the initial list or ignore the subsequent lists; it should not be very important. However, the METHOD-DATA in a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error should be treated separately from this list, as it is part of a multi-hop conversation for a particular mechanism.
We should maintain a list of preauth mechanisms which have failed so that we don't try them again. This list should be separate from the KDC METHOD-DATA and the preauth request state, as we should not retry a failed preauth mechanism after KDC_ERR_PREAUTH_EXPIRED. This list should be discarded on a realm referral, although a realm referral during a preauthentication conversation should be rare. A KDC_ERR_PREAUTH_EXPIRED error should not count as failing. Optimistic preauthentication failure should not count either, as it may only have failed for lack of information from the KDC.
A preauth mechanism may set the reply key as an output. Currently we store this output in the same state variable as is used to cache the gak_fct result. We will need to use a separate variable to support continuing after a preauth mechanism fails, so that we can clear the reply key used by the previous mechanism without also invalidating the gak_fct cached result (and therefore perhaps asking for the password again).
Stage tracking for clpreauth modules
When processing a KDC reply or beginning an exchange, we should keep track of what we are doing: (1) optimistic preauth, (2) sending an unauthenticated message, (3) beginning a preauth exchange using KDC METHOD-DATA, (4) continuing a preauth exchange using the METHOD-DATA in a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error, (5) recovering from a KDC error with e-data, or (6) processing the pa-data in an AS-REP. This stage tracking may also help to organize the get_in_tkt.c logic as we add retries after mechanism failures.
The stage should be made available to clpreauth modules via a callback. During the process method, only stages 1, 3, 4, and 6 will be possible; during the tryagain method, only stage 5 will be possible.
Alternatively, we could create four new clpreauth methods corresponding to the four stages currently served by the process method. These new methods could have shorter signatures if we omit some of the parameters (opt, request, encoded_request_body, encoded_previous_request) and make them available via callbacks. If a module does not supply one of the new methods, preauth2.c would fall back to the process method.
clpreauth module control of mechanism termination
We can add a callback for clpreauth modules to indicate that the mechanism is unfinished; the module would then be responsible for unsetting the flag via the same callback when the mechanism is complete. If the mechanism is incomplete after AS-REP padata processing, the client would abort the exchange.