Adding support for optimistic preauth to kinit

Greg Hudson ghudson at
Sun Apr 4 01:18:05 EDT 2021

On 4/3/21 8:09 PM, Ken Hornstein wrote:
>>From an architectural standpoint, the hard work is already done; the
> library supports this via an already-exposed API in the get_init_creds
> interface (krb5_get_init_creds_opt_set_preauth_list()).  It's just
> that kinit has no way to set this list.

I think the problem is both harder and easier than this.

Each preauth mechanism works differently, and some lend themselves to
optimistic preauth better than others:

* For encrypted timestamp and its FAST variant encrypted challenge, the
KDC's initial method-data is empty, but is accompanied by etype-info
containing the enctype, salt, and s2kparams.  The client can cache those
(but we don't any current mechanism for that) or try to guess them
(default salt, no s2kparams, the client's preferred enctype), but it
should be prepared to retry after getting the proper etype-info, and if
the KDC is doing account lockout for the user principal, the optimistic
failure will count as a strike.

* For PKINIT the KDC's initial method-data is empty and the etype-info
is not needed.  The client needs some configuration and possibly a local
password to unlock its private key, but there's no difference
between optimistic PKINIT preauth and regular PKINIT preauth aside from
the decision to do it.  If optimistic PKINIT fails, it should not be
retried as the method-data won't contain any new information.  (There is
a flow where PKINIT continues on a KDC error containing TD_DH_PARAMETERS
or similar, but that's not the same as trying again using the KDC
initial method-data.)

* SPAKE is even simpler than PKINIT; the client needs no information or
configuration to send an initial negotiation message.  I had actually
planned to make optimistic SPAKE happen by default until I realized it
would prioritize SPAKE over PKINIT when the KDC thinks both are options.

* For SAM-2, the KDC's initial method-data contains a challenge
(including a checksum that can be dictionary-attacked to determine the
password).  The challenge cannot be guessed, so optimistic SAM-2 simply
can't happen.

* For FAST OTP, the KDC's initial method-data contains some challenge
information which is mostly unused as we implemented it (and I don't
think anyone else has implemented it).  So it might be reasonable to do
optimistic FAST OTP with no challenge data if asked for by the user,
although the spec contains no guidance for doing so.

On the flip side, the problem you stated isn't about reducing the number
of round trips, so we don't necessarily need optimistic preauth.  Giving
kinit a way to choose between multiple credentials could be done in
other ways, although we don't have any current gic_opt machinery for them.

A path of lower resistance is to add an option to force a particular
preauth mech (single choice, hard-failing if it isn't available or
doesn't work).  clpreauth modules already declare names like "pkinit"
and "sam2" which could be matched against.  This approach has the
notable failing of requiring users to know something about preauth
mechs--in particular, if the user wants to authenticate with just their
password as registered with kpasswd, they need to know which of three
different preauth mechs to use (encrypted_timestamp,
encrypted_challenge, and spake) based on specific knowledge of their
realm and client capabilities.

A path of higher resistance is to invent names for credential types like
"password", and add a way to query preauth mechs for whether they use
the selected credential type.  Although this might seem more
user-friendly at first, I think it would unfortunately get muddled
fairly quickly.  For instance, "token" could describe a PKCS11 token to
be used with PKINIT, but also a device displaying a second factor for
use with SAM-2 or FAST OTP or (in the possible future) SPAKE.
"password" could describe a password to be used via one of the three
s2k-based mechanisms, or a password that decrypts a PKCS12 private key
or logs into a PKCS11 token for PKINIT.

There is also the option of just having specific kinit options for
specific preauth scenarios; that is, this problem's solution doesn't
have to be runtime-extensible just because we have a runtime-extensible
clpreauth framework (which as far as I know has only ever been used to
replace the PKINIT module with another implementation).  As a point of
reference, Heimdal has specific kinit options and GIC options for
PKINIT, although they don't necessarily seem to fit your problem statement.

More information about the krbdev mailing list