Discussion:
get_cred starting realm
Greg Hudson
2015-04-29 14:07:59 UTC
Permalink
I want to talk about possible designs to solve a problem Nico has
raised.

krb5_get_credentials() assumes that cross-realm TGS requests should
begin with the local TGT principal of the client realm. This assumption
has worked for a long time, but there are two edge cases where it
doesn't: fully-anonymous tickets where the client realm is
WELLKNOWN:ANONYMOUS, and delegated local TGTs for foreign realms. I
don't remember where we have talked about the second case, so it may be
easier to just think about the first case.

It's clear that the solution to this problem involves changing
get_creds, but beyond that there are several choices:

1. We can decide that the creator of a new ccache is responsible for
knowing when it is creating one of these edge cases, and signalling that
by setting a cache config variable indicating the starting realm.
get_creds can check this variable and uses it in preference to the
client realm.

2. get_creds can iterate through the ccache and use the first local TGT
it finds, perhaps after looking up the client realm TGT. This could
yield non-deterministic behavior if the ccache type doesn't preserve
order, in the rare case where the cache contains multiple local TGT
creds. It could also become a performance limiter.

3. We can decide that the ccache layer is responsible for knowing the
starting TGT somehow and making that information available to get_creds.

Nico's approach in Heimdal falls in the third category, with these
specifics: if you initialize a ccache handle and then
krb5_cc_store_cred() a credential for a local TGT, the generic ccache
layer recognizes this and sets a start_realm config variable for the TGT
realm. get_creds checks this variable as in (1). The implementation,
minus some later bugfixes, is at:
https://github.com/heimdal/heimdal/commit/629eeb811a411d703fd6c72b7fcab74967c7a762

I am wary of putting this kind of magic into the generic ccache
store_creds() dispatch. I had been assuming that the best solution
would be in the first category, but perhaps it is unreasonable to ask
ccache creators to do more than store a TGT. What do other people
think?
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 16:28:40 UTC
Permalink
Post by Greg Hudson
It's clear that the solution to this problem involves changing
Not just. Various other functions in Heimdal also needed to get a
better sense of "starting realm", and my sketch of patches for MIT
Kerberos shows the same there.
Post by Greg Hudson
1. We can decide that the creator of a new ccache is responsible for
knowing when it is creating one of these edge cases, and signalling that
by setting a cache config variable indicating the starting realm.
get_creds can check this variable and uses it in preference to the
client realm.
2. get_creds can iterate through the ccache and use the first local TGT
it finds, perhaps after looking up the client realm TGT. This could
yield non-deterministic behavior if the ccache type doesn't preserve
order, in the rare case where the cache contains multiple local TGT
creds. It could also become a performance limiter.
We know at least one ccache type with non-deterministic iteration order:
MSLSA. (Though it may not permit insertion.)

The Heimdal SQLite3-based ccache uses timestamps for ordering, not
rowids, and it doesn't have autoincrement, therefore it's deterministic
for iteration and preserves insertion order IFF time is monotonic.
Unfortunately it uses seconds since the epoch as its representation of
time, which means that iteration order for insertions in the same second
is non-deterministic.
Post by Greg Hudson
3. We can decide that the ccache layer is responsible for knowing the
starting TGT somehow and making that information available to get_creds.
Nico's approach in Heimdal falls in the third category, with these
It's actually (1) and (3). The application, if it knows better, can
store the ccconfig that names the starting realm, and this will be
honored, but the ccache layer will pick the first TGT stored as the
default starting realm until the application tells it otherwise.
Post by Greg Hudson
specifics: if you initialize a ccache handle and then
krb5_cc_store_cred() a credential for a local TGT, the generic ccache
layer recognizes this and sets a start_realm config variable for the TGT
realm. get_creds checks this variable as in (1). The implementation,
https://github.com/heimdal/heimdal/commit/629eeb811a411d703fd6c72b7fcab74967c7a762
Note that the application can also call krb5_cc_set_config() to force a
different start-realm setting.
Post by Greg Hudson
I am wary of putting this kind of magic into the generic ccache
store_creds() dispatch. I had been assuming that the best solution
would be in the first category, but perhaps it is unreasonable to ask
ccache creators to do more than store a TGT. What do other people
think?
It's not magic. It's a heuristic that a) works for all existing apps,
b) works for all existing ccaches (that support initialization and
insertion), c) allows applications to override the heuristic.

It's no more magic that any of the other ccconfigs we store now, nor
time offset, and so on.

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Simo Sorce
2015-04-29 16:51:54 UTC
Permalink
Post by Nico Williams
Post by Greg Hudson
It's clear that the solution to this problem involves changing
Not just. Various other functions in Heimdal also needed to get a
better sense of "starting realm", and my sketch of patches for MIT
Kerberos shows the same there.
Post by Greg Hudson
1. We can decide that the creator of a new ccache is responsible for
knowing when it is creating one of these edge cases, and signalling that
by setting a cache config variable indicating the starting realm.
get_creds can check this variable and uses it in preference to the
client realm.
2. get_creds can iterate through the ccache and use the first local TGT
it finds, perhaps after looking up the client realm TGT. This could
yield non-deterministic behavior if the ccache type doesn't preserve
order, in the rare case where the cache contains multiple local TGT
creds. It could also become a performance limiter.
MSLSA. (Though it may not permit insertion.)
The Heimdal SQLite3-based ccache uses timestamps for ordering, not
rowids, and it doesn't have autoincrement, therefore it's deterministic
for iteration and preserves insertion order IFF time is monotonic.
Unfortunately it uses seconds since the epoch as its representation of
time, which means that iteration order for insertions in the same second
is non-deterministic.
The KEYRING collection cache order is also probably non-deterministic.
Post by Nico Williams
Post by Greg Hudson
3. We can decide that the ccache layer is responsible for knowing the
starting TGT somehow and making that information available to get_creds.
Nico's approach in Heimdal falls in the third category, with these
It's actually (1) and (3). The application, if it knows better, can
store the ccconfig that names the starting realm, and this will be
honored, but the ccache layer will pick the first TGT stored as the
default starting realm until the application tells it otherwise.
+1
Post by Nico Williams
Post by Greg Hudson
specifics: if you initialize a ccache handle and then
krb5_cc_store_cred() a credential for a local TGT, the generic ccache
layer recognizes this and sets a start_realm config variable for the TGT
realm. get_creds checks this variable as in (1). The implementation,
https://github.com/heimdal/heimdal/commit/629eeb811a411d703fd6c72b7fcab74967c7a762
Note that the application can also call krb5_cc_set_config() to force a
different start-realm setting.
Post by Greg Hudson
I am wary of putting this kind of magic into the generic ccache
store_creds() dispatch. I had been assuming that the best solution
would be in the first category, but perhaps it is unreasonable to ask
ccache creators to do more than store a TGT. What do other people
think?
It's not magic. It's a heuristic that a) works for all existing apps,
b) works for all existing ccaches (that support initialization and
insertion), c) allows applications to override the heuristic.
It's no more magic that any of the other ccconfigs we store now, nor
time offset, and so on.
Having the library take care of it by default and allow applications to
override seem the best compromise between usability and correctness.

Simo.
--
Simo Sorce * Red Hat, Inc * New York

_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Benjamin Kaduk
2015-04-29 17:01:45 UTC
Permalink
Post by Nico Williams
Post by Greg Hudson
It's clear that the solution to this problem involves changing
Not just. Various other functions in Heimdal also needed to get a
better sense of "starting realm", and my sketch of patches for MIT
Kerberos shows the same there.
Post by Greg Hudson
1. We can decide that the creator of a new ccache is responsible for
knowing when it is creating one of these edge cases, and signalling that
by setting a cache config variable indicating the starting realm.
get_creds can check this variable and uses it in preference to the
client realm.
"What if the creator of a new FILE: ccache is a java application?"
Post by Nico Williams
Post by Greg Hudson
2. get_creds can iterate through the ccache and use the first local TGT
it finds, perhaps after looking up the client realm TGT. This could
yield non-deterministic behavior if the ccache type doesn't preserve
order, in the rare case where the cache contains multiple local TGT
creds. It could also become a performance limiter.
MSLSA. (Though it may not permit insertion.)
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
Post by Nico Williams
The Heimdal SQLite3-based ccache uses timestamps for ordering, not
rowids, and it doesn't have autoincrement, therefore it's deterministic
for iteration and preserves insertion order IFF time is monotonic.
Unfortunately it uses seconds since the epoch as its representation of
time, which means that iteration order for insertions in the same second
is non-deterministic.
Post by Greg Hudson
3. We can decide that the ccache layer is responsible for knowing the
starting TGT somehow and making that information available to get_creds.
Nico's approach in Heimdal falls in the third category, with these
It's actually (1) and (3). The application, if it knows better, can
store the ccconfig that names the starting realm, and this will be
honored, but the ccache layer will pick the first TGT stored as the
default starting realm until the application tells it otherwise.
This is starting to sound like a reasonable approach.

-Ben
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 17:18:26 UTC
Permalink
Post by Benjamin Kaduk
"What if the creator of a new FILE: ccache is a java application?"
Then whatever knowledge of "start realm" was always going to be lost,
and will continue to be lost until the application is enhanced. Interop
with such an app continues using traditional behavior (see below).

Picking a start realm at kinit time (e.g., the Java application is using
Kerberos via a JNI interface) helps.

And if the Java application is using native Java Kerberos then we
fallback on the same old behavior: use a TGT for the client principal's
realm.

Because Heimdal (and hopefully soon MIT) had to interop with writers of
ccaches that don't store this ccconfig, the correct behavior is to
fallback on the pre-start-realm-ccconfig behavior when that ccconfig is
missing. That's what I implemented for Heimdal.

To recap:

- maximum interop:

- readers that need a starting-realm notion look for the new
start-realm ccconfig using krb5_cc_get_config()

if the find it, they use that, else the do what they always did
(use the client principal's realm name as the starting realm)

- reasonable default start realm selection at kinit time:

- krb5_cc_initialize() marks the ccache as needing a starting realm

- krb5_cc_store_cred() on an initialized ccache picks the first local
TGT's realm as the starting realm and stores that in a start-realm
ccconfig, then clears the flag set by krb5_cc_initialize()

- application-level knowledge of starting realm is honored:

- krb5_cc_store_cred() always allows the application to store a
start-realm ccconfig (usually via krb5_cc_set_config(), of course)
to override the default start-realm selection
Post by Benjamin Kaduk
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
Does it permit storing of ccconfigs? (That would be handy.)

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Benjamin Kaduk
2015-04-29 22:24:07 UTC
Permalink
Post by Nico Williams
Post by Benjamin Kaduk
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
Does it permit storing of ccconfigs? (That would be handy.)
2060 if (krb5_is_config_principal(context, creds->server)) {
2061 /* mslsa cannot store config creds, so we have to bail.
2062 * The 'right' thing to do would be to return an appropriate error,
2063 * but that would require modifying the calling code to check
2064 * for that error and ignore it.
2065 */
2066 return KRB5_OK;
2067 }

Though, I expect that code was written ten or fifteen years ago and the
comment may be stale.

-Ben
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 22:57:52 UTC
Permalink
Post by Benjamin Kaduk
Post by Nico Williams
Post by Benjamin Kaduk
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
Does it permit storing of ccconfigs? (That would be handy.)
2060 if (krb5_is_config_principal(context, creds->server)) {
2061 /* mslsa cannot store config creds, so we have to bail.
2062 * The 'right' thing to do would be to return an appropriate error,
2063 * but that would require modifying the calling code to check
2064 * for that error and ignore it.
2065 */
2066 return KRB5_OK;
2067 }
Though, I expect that code was written ten or fifteen years ago and the
comment may be stale.
Unless the LSA blows up (it shouldn't) or kills the caller (it
shouldn't), what's the point of stubbing this out? Just try it. In the
worst case scenario it fails.
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Benjamin Kaduk
2015-04-30 02:35:15 UTC
Permalink
Post by Nico Williams
Post by Benjamin Kaduk
Post by Nico Williams
Post by Benjamin Kaduk
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
Does it permit storing of ccconfigs? (That would be handy.)
2060 if (krb5_is_config_principal(context, creds->server)) {
2061 /* mslsa cannot store config creds, so we have to bail.
2062 * The 'right' thing to do would be to return an appropriate error,
2063 * but that would require modifying the calling code to check
2064 * for that error and ignore it.
2065 */
2066 return KRB5_OK;
2067 }
Though, I expect that code was written ten or fifteen years ago and the
comment may be stale.
Unless the LSA blows up (it shouldn't) or kills the caller (it
shouldn't), what's the point of stubbing this out? Just try it. In the
worst case scenario it fails.
The "real" operation corresponding logically to storing an extant ticket
into the LSA is KerbSubmitTicket(), called next after this, but if that
fails, we attempt to coerce the LSA into retrieving a corresponding ticket
on our behalf; I expect this latter step would behave poorly (probably
hanging for a DNS timeout) for config entries.

That said, I don't see an issue with moving the config check after
KerbSubmitTicket() (but before GetMSCacheTicketFromMITCred()).

The check for config principals was added in December 2011, possibly
because that was the first time work was done on KfW since config entries
had been added to the tree. I don't know how much testing of various
cases was done prior to that commit
(0d2e965bb94fe7df11104fa462104f4eb19086a3).

-Ben
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 23:19:37 UTC
Permalink
Post by Benjamin Kaduk
Post by Nico Williams
Post by Benjamin Kaduk
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
Does it permit storing of ccconfigs? (That would be handy.)
2060 if (krb5_is_config_principal(context, creds->server)) {
2061 /* mslsa cannot store config creds, so we have to bail.
2062 * The 'right' thing to do would be to return an appropriate error,
2063 * but that would require modifying the calling code to check
2064 * for that error and ignore it.
2065 */
2066 return KRB5_OK;
2067 }
Though, I expect that code was written ten or fifteen years ago and the
comment may be stale.
[resend]

Unless the LSA blows up (doubtful) or kills the caller (doubtful),
what's the point of stubbing this out? Try it. In the worst case it
will fail.

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Jeffrey Altman
2015-04-30 04:11:12 UTC
Permalink
Post by Benjamin Kaduk
Post by Nico Williams
MSLSA. (Though it may not permit insertion.)
The LSA itself permits insertion; our MSLSA interface to it may not be
quite so generous, though I don't remember offhand.
The LSA Kerberos functions do have some caching properties but they are
primarily not a credential cache. The LSA Kerberos functions are a
service ticket fetching API. The MSLSA krb5_ccache implementation is a
wrapper for the Microsoft Kerberos implementation that permit
applications built against MIT's Kerberos and GSS libraries to make use
of the Microsoft Kerberos implementation without source code changes.

If the Microsoft Kerberos implementation is unable to obtain the
requested ticket, then the MIT krb5 library will attempt to obtain one
itself. The ability to submit the resulting ticket (if any) to the LSA
and retrieve it again is dependent upon:

* how the MSLSA shim was built

* the OS version and SKU

* policy

The contents of the LSA can be destroyed at any point by any application
and as a result of a screen unlock.

In addition, the MSLSA can appear to store credentials for multiple
client principals. The application visible contents are not all from
the same store. The view of the application is actually a mashup of
three different credential stores (two of which the application cannot
alter) and the store that user application can alter can be masked by
the contents of the other two.

As a result I do not consider storing hints in the LSA to be stable or
reliable. When using the LSA non-Microsoft hints IMHO should be stored
outside the LSA.

Jeffrey Altman
Greg Hudson
2015-04-29 17:47:49 UTC
Permalink
Post by Nico Williams
Post by Greg Hudson
I am wary of putting this kind of magic into the generic ccache
store_creds() dispatch. I had been assuming that the best solution
would be in the first category, but perhaps it is unreasonable to ask
ccache creators to do more than store a TGT. What do other people
think?
It's not magic. It's a heuristic that a) works for all existing apps,
b) works for all existing ccaches (that support initialization and
insertion), c) allows applications to override the heuristic.
Here is why I consider it to be magic:

* The caller asks for one cred to be stored, but the result is that two
creds are stored, as observed by the cache type and by future iterations.

* The generic cache store function used to be just a dispatch; now it
implements some of the responsibilities of the ccache layer. The cache
type cannot distinguish between creds intentionally stored by the caller
and synthetic config entries generated by the generic cache layer.

* If a caller copies a cache by iterating and storing, the cache type
for the destination might observe a start realm config entry being
stored twice (perhaps with different values), or perhaps only once,
depending on the iteration order for the source.

* The starting-realm entry is only synthesized if you store_cred after
initializing the same handle. If you open, initialize, close, open, and
store, no starting-realm entry is synthesized.

These are not necessarily practical issues. But it's unquestionable
that any formal description of the new contract for the initialize,
store, get_config, and iteration functions would be significantly more
fiddly than the old contract.
Post by Nico Williams
It's no more magic that any of the other ccconfigs we store now, nor
time offset, and so on.
It is definitely more magic than other cache configs, which all come
from higher layers. Regardless, I would describe the implementation of
cache configs as magic. Cache types can only distinguish between normal
creds and config entries by scraping. Config entries appear when
iterating over credentials--which of course has useful consequences for
callers who copy caches, and negative consequence sfor callers like
klist who display them.

The way time offsets are handled is seriously magic, with a ton of
unfortunate edge cases for cache collections.
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 18:05:01 UTC
Permalink
Post by Greg Hudson
Post by Nico Williams
Post by Greg Hudson
I am wary of putting this kind of magic into the generic ccache
store_creds() dispatch. I had been assuming that the best solution
would be in the first category, but perhaps it is unreasonable to ask
ccache creators to do more than store a TGT. What do other people
think?
It's not magic. It's a heuristic that a) works for all existing apps,
b) works for all existing ccaches (that support initialization and
insertion), c) allows applications to override the heuristic.
* The caller asks for one cred to be stored, but the result is that two
creds are stored, as observed by the cache type and by future iterations.
Eh, this already happens, no? E.g., with the referral realm business.
Post by Greg Hudson
* The generic cache store function used to be just a dispatch; now it
implements some of the responsibilities of the ccache layer. The cache
type cannot distinguish between creds intentionally stored by the caller
and synthetic config entries generated by the generic cache layer.
True, but if it ever becomes a problem then the SPI can be extended. I
get that that extends to the KCM/CCAPI protocol, but I doubt this will
ever be necessary. Why would the ccache need to make that distinction?

Alternatively we could have two ccconfigs for this instead of one, and
then have krb5_cc_get_config() for the start-realm ccconfig try up to
both.
Post by Greg Hudson
* If a caller copies a cache by iterating and storing, the cache type
for the destination might observe a start realm config entry being
stored twice (perhaps with different values), or perhaps only once,
depending on the iteration order for the source.
Not if the source implements krb5_cc_remove_cred().
Post by Greg Hudson
* The starting-realm entry is only synthesized if you store_cred after
initializing the same handle. If you open, initialize, close, open, and
store, no starting-realm entry is synthesized.
True. (This too could be handled. But does this ever happen?)
Post by Greg Hudson
These are not necessarily practical issues. But it's unquestionable
that any formal description of the new contract for the initialize,
store, get_config, and iteration functions would be significantly more
fiddly than the old contract.
I think they are not practical issues.

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Greg Hudson
2015-04-29 21:54:51 UTC
Permalink
Post by Nico Williams
Post by Greg Hudson
* The caller asks for one cred to be stored, but the result is that two
creds are stored, as observed by the cache type and by future iterations.
Eh, this already happens, no? E.g., with the referral realm business.
Not inside the credential cache layer, no. Those artifacts are created
by a higher layer (get_creds).
Post by Nico Williams
Post by Greg Hudson
* If a caller copies a cache by iterating and storing, the cache type
for the destination might observe a start realm config entry being
stored twice (perhaps with different values), or perhaps only once,
depending on the iteration order for the source.
Not if the source implements krb5_cc_remove_cred().
Even if the source implements krb5_cc_remove_cred(). The destination
cache may see one entry synthesized by the generic cache layer when the
local TGT is stored, and another when the start-realm config cred is
explicitly stored. The first value could be wrong, and the destination
cache needs to be able to overwrite it with the second value.

Of course this only happens if the iteration over the source reaches the
local TGT (or a different local TGT!) before reaching the start-realm
config cred.
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 22:54:08 UTC
Permalink
What is the problem with "synthesized" ccconfigs?

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Greg Hudson
2015-04-30 19:40:40 UTC
Permalink
Post by Greg Hudson
Post by Nico Williams
Post by Greg Hudson
* The caller asks for one cred to be stored, but the result is that two
creds are stored, as observed by the cache type and by future iterations.
Eh, this already happens, no? E.g., with the referral realm business.
Not inside the credential cache layer, no. Those artifacts are created
by a higher layer (get_creds).
I was wrong about this; there actually is logic in the generic ccache
store_cred() and retrieve_cred functions for the referral realm.
Apologies for incorrectly contradicting Nico on this point.

I will revise my opinion to: I'm not really happy with solving these
problems inside a layer which was originally designed just to be a
container for credentials and a default client principal. I think we
have an unfortunate history in MIT krb5 of adding complexity to our
lower-layer contracts in order to solve higher-layer problems. We
definitely have a history of this in the ccache layer (referrals, config
entries encoded as creds, timestamps), and we have paid costs for that
history. It may not hurt much to add one more bit of complexity to an
already complex lower layer, but I would like to at least explore other
options.

I think the two other viable options are:

1. Make kinit -n and krb5_gss_accept_sec_context() responsible for
setting a start-realm config entry when the local TGT realm differs from
the client principal realm. Basically, treat the edge cases as
exceptional and force them to be addressed at the highest possible layers.

2. Ditch the cache config entry, and instead add a new ccache function
to retrieve the main local TGT (which is defined to be the first local
TGT stored in the cache). Each type would be responsible for
implementing this, either by being order-preserving and just iterating
over the creds until it finds a local TGT, or by remembering which local
TGT was first.

The second option has a pitfall: if a ccache contains multiple local
TGTs (unusual) and is copied using iterate-and-store, and the source
ccache type isn't order-preserving, then the local TGTs might not be
stored in the same order and the wrong one might be treated as the main
TGT. I don't expect that this problem would occur in practice, but we
might look for a way to solve it at least in krb5_cc_copy_creds() and
krb5_cc_move().
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-30 20:23:03 UTC
Permalink
Post by Greg Hudson
I was wrong about this; there actually is logic in the generic ccache
store_cred() and retrieve_cred functions for the referral realm.
Apologies for incorrectly contradicting Nico on this point.
Well, I should have noticed earlier. No need to apologize.
Post by Greg Hudson
I will revise my opinion to: I'm not really happy with solving these
problems inside a layer which was originally designed just to be a
container for credentials and a default client principal. I think we
have an unfortunate history in MIT krb5 of adding complexity to our
lower-layer contracts in order to solve higher-layer problems. We
definitely have a history of this in the ccache layer (referrals, config
entries encoded as creds, timestamps), and we have paid costs for that
history. It may not hurt much to add one more bit of complexity to an
already complex lower layer, but I would like to at least explore other
options.
I'd be happy to have a brand new credentials cache API (and SPI) and
file formats that rock. However, backwards-compatibility is still an
imperative (ABI, source, and file format backwards compatibility, with
file format interoperability added in as a requirement), so... we can't
have that.

Even if we could, we'd still want to put "magic" at the new ccache layer
just because of the DRY principle and to simplify the API for third
parties. (I expand on this line of argument below.)

The start-realm ccconfig proposal... does just that: simplifies the API
for third parties (e.g., no need to modify pam_krb5) because it reduces
the need to repeat oneself.
Post by Greg Hudson
1. Make kinit -n and krb5_gss_accept_sec_context() responsible for
setting a start-realm config entry when the local TGT realm differs from
the client principal realm. Basically, treat the edge cases as
exceptional and force them to be addressed at the highest possible layers.
I'd live with this because it'd at least interop with Heimdal and could
be implemented by other stacks that implement the FILE ccache format.

But I won't contribute patches to implement (1) because I'd have to also
submit patches for third-party applications like pam_krb5, and I'm not
interested in boiling oceans in general. See more below.
Post by Greg Hudson
2. Ditch the cache config entry, and instead add a new ccache function
to retrieve the main local TGT (which is defined to be the first local
TGT stored in the cache). Each type would be responsible for
implementing this, either by being order-preserving and just iterating
over the creds until it finds a local TGT, or by remembering which local
TGT was first.
I strongly object to (2) on these grounds:

- It depends on ccaches having an iteration order that reflects
insertion order;

- it does not allow one to change the starting realm except by
initializing the/a new ccache and then inserting local TGTs in the
desired order.

This is a disgusting API complification[*], and an example of why
pushing some things to lower layers is exactly the right thing to do.

(1) also is a complification: because it requires upper layers / apps to
do more work.
Post by Greg Hudson
The second option has a pitfall: if a ccache contains multiple local
TGTs (unusual) and is copied using iterate-and-store, and the source
It's not unusual if one has implemented destination-only credential
delegation (a step along the way to doing that is fetching a local TGT
for the destination realm and storing it in the ccache, then asking for
a forwarded destination TGT).

One might want a configuration where the start-realm is not the same as
the client principal's realm even when a local TGT for it is available
or can be had. Having to implement such a configuration/policy by
pushing and repeating the implementation in every possible kinit-alike
is an obnoxious complification too. At that rate one might think that
one might as well open-code everything everywhere...

Libraries exist precisely to reduce the amount repetition. In this case
that means that the default start-realm behavior should indeed be
implemented by "magic" in krb5_cc_store_cred(). Except it's not magic.
It's exactly the right thing to do, and it's hard to call that magic.
Post by Greg Hudson
ccache type isn't order-preserving, then the local TGTs might not be
stored in the same order and the wrong one might be treated as the main
TGT. I don't expect that this problem would occur in practice, but we
might look for a way to solve it at least in krb5_cc_copy_creds() and
krb5_cc_move().
[*] I'm not even the first to coin that neologism. But there's a reason
that 'complification' is not in the dictionary: we generally don't
like to deliberately complicate things. No, 'complication' is not a
synonym of 'complification', not to me.
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-30 20:45:17 UTC
Permalink
Post by Nico Williams
Post by Greg Hudson
1. Make kinit -n and krb5_gss_accept_sec_context() responsible for
setting a start-realm config entry when the local TGT realm differs from
the client principal realm. Basically, treat the edge cases as
exceptional and force them to be addressed at the highest possible layers.
I'd live with this because it'd at least interop with Heimdal and could
be implemented by other stacks that implement the FILE ccache format.
But I won't contribute patches to implement (1) because I'd have to also
submit patches for third-party applications like pam_krb5, and I'm not
interested in boiling oceans in general. See more below.
I misread Greg's (1) proposal. It's fine and requires no changes to
existing applications not in-tree. I'd still prefer always writing the
start-realm ccconfig because for FILE ccaches doing so means that it
will always be found quickly.

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 17:25:37 UTC
Permalink
Post by Greg Hudson
krb5_get_credentials() assumes that cross-realm TGS requests should
begin with the local TGT principal of the client realm. This assumption
has worked for a long time, but there are two edge cases where it
doesn't: fully-anonymous tickets where the client realm is
WELLKNOWN:ANONYMOUS, and delegated local TGTs for foreign realms. I
don't remember where we have talked about the second case, so it may be
easier to just think about the first case.
The delegation case we call "delegating a destination-only TGT", though
really, it's a TGT that can't be used to a) reach services at the client
principal's realm (because of loop detection by TGSes), b) reach
services not reachable from the target service's realm (because of
missing trust relations). (a) is a useful property, even if (b)
sometimes has the same effect.

This is a form of constrained delegation.

Destination-only TGTs have the same propery of TGTs for the fully-
anonymous client principal: the realm of the TGT does not match the
client principal's.

The start-realm ccconfig business helps clients select the same starting
TGT regardless of ccache iteration order determinism/non-determinism.

Delegation of destination-only TGTs requires additional code (to decide
when to do it, and to then get such forwarded tickets for delegation).
Viktor has patches to implement destination-only TGT delegation. It's
quite convenient for several use cases.

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Nico Williams
2015-04-29 17:36:42 UTC
Permalink
I should also add that every case I know of today where an application
initializes a ccache (with any implementation, Heimdal, MIT, GNU, Java)
the first local TGT stored *is* the TGT that is "intended" as the one to
use for starting TGS requests (for that realm, certainly, and for any
TGS clients that support referrals chasing from a "home" realm).

KRB-CRED supports forwarding multiple tickets, but in practice we only
ever forward a single TGT. And kinit-like applications generally only
store the one TGT they fetch.

The effect is that the first local TGT stored after initializing is
always (today) the TGT that's intended as the starting TGT.

There may be (there are) ccache copy/refresh applications that
initialize a ccache and store refreshed versions of all tickets found
in the original ccache. Typically such applications iterate through the
source ccache, and they will tend to find the "intended" starting TGT as
the first TGT (in most ccache types). And, of course, if the
start-realm ccconfig is present in the source ccache, then it will be
preserved.

So in practice the heuristic I implemented will interop correctly with
implementations that don't implement the start-realm ccconfig.

We should update applications to store an explicit start-realm ccconfig
when that would be different from the heuristic (or even always, when it
is known). In practice the default heuristic works for kinit and
gss_acquire_cred_with_password() and so on, so no applications will need
modification initially.

Nico
--
_______________________________________________
krbdev mailing list ***@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev
Loading...