heimdal icon indicating copy to clipboard operation
heimdal copied to clipboard

Rethinking kinit behavior -- specifying user intent

Open nicowilliams opened this issue 3 years ago • 2 comments

We're considering changes to Heimdal's kinit's default behaviors. We want to make it more useful, less surprising, and more user-friendly.

Final design

  • kinit

    • add behavior selection options
      • --default-for-principal
      • --search
      • --unique
    • the default behavior when none of those new options is given is to honor the -c CACHE/--cache=CACHE/$KRB5CCNAME/configured default cache
    • if any of those new options is used, then the given cache is only used to identify a collection
    • all cache types are collections
    • if --search is given, then search for a cache in the collection identified by the -c CACHE/..., and use it if found, else use the --default-for-principal behavior unless --unique is given
    • if --default-for-principal is given, the use a cache in the collection identified by the -c CACHE/... with a subsidiary name derived from the name of the principal we're getting credentials for
    • if --unique is given, use a new, unique cache in the collection identified by the -c CACHE/...
    • when using the cache identified by the -c CACHE/..., refuse to overwrite it if it has credentials for a different principal
    • when giving a command to kinit, as always, use a new unique cache, but error out if --default-for-principal or --search are given
  • klist

    • add --default-for=principal and --search-for=principal options to klist
  • kswitch

    • try krb5_cc_default_for() first when -p principal / --principal=principal is given, then fallback on krb5_cc_match()

Motivation

There are cases when MIT's and Heimdal's kinit (prior to this PR) will honor the -c CACHE/--cache=CACHE/$KRB5CCNAME, and cases where they'll search the user's credentials cache collection, and cases where they will create a "new unique" ccache different from what the user might have intended, and other cases where MIT will refuse to write out a ccache while Heimdal will not refuse.

  • That is a hot mess and terribly confusing UI/UX. We'd like to make this better.

Another motivation is that "new unique" ccache names become garbage that piles up and has to be collected. This is not a friendly UX. As well, "new unique" ccache names are meaningless but leak into UIs anyways -- another poor UX.

Relatedly, setting KRB5CCNAME or using -c-like options when doing manual identity selection... requires running klist -l to pick a cache name. But if we name the caches after the principals for which they store credentials, then one can just type, for example, KRB5CCNAME=KCM:${UID}:${USER}@MY.REALM.EXAMPLE ssh somewhere.

So we want to a) make sure that user intent is clear, b) make it easy to name ccaches after principals so it's easy to find the right ccache for each context, and c) provide some safety belt and suspenders.

Background:

  • Cache collections exist for several reasons:
    • To support having credentials for multiple different principals. (In principle a single credentials cache can store tickets for multiple different client principals, but in practice this is not supported by any implementations.)
    • To use an atomic "default cache switch" operation on collections to work around lack of atomic ccache initialization.
  • In both, MIT and Heimdal, krb5_cc_default() and krb5_cc_default_name() use the value of KRB5CCNAME from the environ if it's set, else a configured default_ccache_name [libdefaults] param, else the compiled-in default ccache for the configured or compiled-in default ccache type.
  • In Heimdal we have krb5_cc_move() to rename ccaches, and most ccache types in Heimdal support atomic krb5_cc_move()s -- we leave it to the ccache type to get this right, and if not, too bad.
  • MIT does not appear to have atomic ccache renaming operations. Neither does the sssd kcm. Neither does CCAPI.
  • In Heimdal, every ccache type is a collection type (yes, even FILE!). Not so in MIT.
    • Heimdal has a --default-for-principal kinit option in master, and corresponding APIs, for creating a ccache in a given collection but with a cache name derived from the principal name whose credentials will be stored in it -- this makes use of collections user-friendly because the cache names reflect the principal whose credentials they contain.

Proposed UX -- Explicit User Intent

  • The user must specify intent. There are only so many behaviors to choose from, so let's have options for all of them, a reasonable default, and let's not deviate from user intent.

  • By default kinit should operate on the named ccache. Any alternative requires the use of specific options. No more "some cases create a new unique" cache silently.

  • Using TYPE: (e.g., KCM:) or equivalent collection names (e.g., KCM:12345 when the process' real UID is 12345) refers to the whole collection and denotes user intent to use specific caches in that collection whose names are derived from those of the client principals that they store credentials for.

    When using such a "whole collection" ccache name, using kinit to initialize a ccache with some principal's credentials should create a cache with a name based on that principal name (as if --default-for-principal had been given), and should make that the current ccache for the whole collection unless --no-change-default is given.

  • If the ccache exists and has creds for a different principal than we would be storing creds for, then fail unless other options (--overwrite) alter this understanding.

Proposed new library APIs/behaviors

  • Make TYPE: a ccache name that refers to a) the whole collection for the user for that cache type, and b) the current default ccache for that collection (e.g., KCM: would be "the whole collection of caches and the current default ccache in that collection as well, for the real user ID of the current process").

    This is already so for some, but not all ccache types in Heimdal.

    This will require a new krb5_cc_ops method, and a corresponding krb5_cc API.

  • Make every ccache type have and report a specific residual ccache name that also refers to the whole collection and its default (e.g., if the current real UID is 12345 then KCM:12345 would be equivalent to KCM:, while for FILE that would be /tmp/krb5cc_%{UID}). The ccache types should report this value with tokens unexpanded.

Proposed new kinit behavior

See above.

But what about atomicity?

We get atomicity for free in Heimdal for ccache types that support atomic ccache rename. However, not all do.

For ccache types that don't support atomic ccache rename, the above proposal will simply mean no atomicity. We could have kinit act differently for such ccache types, however.

Existing uses of "new unique then switch" still have atomicity issues even though the "switch" operation is atomic. The reason is that when users use KRB5CCNAME to pick a specific ccache from their collection to use in some application, when credentials in that cache are near expiration and the user goes to kinit -R them, if the operation to install the new credentials is non-atomic, then there is a race condition anyways.

See also Greg's comment about atomicity.

nicowilliams avatar Jan 26 '22 05:01 nicowilliams

The asprintf() issue is probably very common in our tree.

nicowilliams avatar Feb 01 '22 01:02 nicowilliams

MIT does not appear to have atomic ccache renaming operations. Neither does the sssd kcm. Neither does CCAPI.

We do as of commit 371f09d4bf4ca0c7ba15c5ef909bc35307ed9cc3, for FILE, DIR, MEMORY, and KCM if the daemon supports it (but none of them do yet, as far as I know).

greghudson avatar Feb 02 '22 06:02 greghudson