[AFS3-std] New Version Notification for draft-wilkinson-afs3-rxgk-06.txt (fwd)

Benjamin Kaduk kaduk@MIT.EDU
Thu, 11 Jul 2013 15:40:47 -0400 (EDT)


On Thu, 11 Jul 2013, Jeffrey Hutzelman wrote:

> Forgive me if this is a little unpolished; it's getting late and I'm
> trying to get a new fileserver set up by several hours ago.

That's okay, mine was a bit unpolished due to lateness as well.

> On Thu, 2013-07-11 at 00:19 -0400, Benjamin Kaduk wrote:
>
>>> - In step 3, you don't say anything about the case where the major
>>>  status code from GSS_Accept_sec_context indicates an error.
>>
>> This was something of a conscious decision, as the client need not know
>> the server's gss_accept_sec_context() return value directly.
>
> Sure.  But the negotiation must fail.  Remember that although this is
> structured as a client making a bunch of RPCs, it's not good enough to
> describe the process from the client's point of view.  If
> GSS_Accept_sec_context returns an error, then the _server_ has to fail
> the exchange, no matter what the client does from then on (of course,
> the client is free to start over).

Okay.  We should probably have some generic text on how the server should 
fail the negotiation.

> BTW, this may also mean that the server needs to do some sort of replay
> protection with its opaque tokens.  An exported partially-established

The GSS tokens, or the ones used to serialize server state across 
nominally-stateless RPCs (the "opaque_out" and "opaque_in" arguments of 
the GSSNegotiate RPC)?  I assume the latter.

> context ought to be re-importable only once.  I don't really recall what
> the API spec says about dealing with this.

The GSS tokens should only be usable once, I agree.
In practice, this probably means that you can't call GSSNegotiate against 
one vlserver and then try to finish against a different vlserver.

My rough plan for implementing multi-round-trip mechanisms on the 
server-side was to cache partially-constructed GSS security contexts, with 
a cap on how many can be cached at once and an expiration timer on them. 
That would eliminate any need to either export/import the 
partially-constructed context or replay GSS tokens.

[server reporting failure covered below]

>> You are taking issue with the synthesis of GSS_S_BAD_QOP?
>
> Yes.

I think I ran that by tlyu/ghudson and it seemed okay.  I can 
double-check.

I suppose this would be a reasonable thing to put in the errorcode field 
of the ClientInfo, though part of the point of the exchange is that we are 
relying on the contents of the ClientInfo to be integrity-protected.

>> To represent this error case (GSS_Accept_sec_context returns
>> GSS_S_COMPLETE but ret_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG) !=
>> GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG) as a com_err error, I believe we would
>> need to introduce a new RXGK error code.  Do you have suggestions for this
>> error code name and text?
>
> We don't already have a semi-general "bad context" error?

The most generic thing we have is RXGK_INCONSISTENCY, "Security module 
structure inconsistent".  This isn't really a BAD_TOKEN or a SEALED_INCON, 
I think.

>>> - In step 4, if the GSSNegotiate() fails for any reason, the client
>>>  should consider the negotiation to have failed.
>>
>> Well, the client should not decide to downgrade to rxkad because
>> GSSNegotiate returns -1!  I can add explicit text for what to do if the
>> RPC fails if you want, though I think the intent was that the last
>> paragraph of 6.2 would cover this case.
>
> Oh, hm, I see.

It's a bootstrapping problem; we can't securely report an error until we 
have a GSS context established, so any errors incurred during the 
negotiation are in some sort of limbo.
The client will presumably need to retry a few times and then report 
failure to the user.


>>> - Don't conflate GSS_S_CONTINUE_NEEDED with an output token.  The
>>>  GSS_S_CONTINUE_NEEDED flag indicates that another call with an input
>>>  token is expected, not that an output token was provided.  So...
>>
>> Right.
>>
>>>  + If GSS_Init_sec_context produced a token when the server wasn't
>>>    expecting one, or failed to produce one when it was, that is an
>>>    error, and negotiation fails.
>>>
>>>  + If GSS_Accept_sec_context produced a token when the client was
>>>    not expecting one, or failed to produce one when it was, that is
>>>    an error, and negotiation fails.
>>
>> These will both be caught by the counterparty's next GSS call and need not
>> be handled explicitly.
>
> I'm not convinced.  This is an authentication exchange with an untrusted
> party; if something fails locally, you have to fail it locally, not
> depend on the other party to fail.

I guess (part of) the problem is that our story for reporting errors from 
the server to the client is lousy (the bootstrapping problem abpve).  I 
think there probably will need to be some place for Rx aborts, and we'll 
need to make the client do the least bad thing.

>> Hmm, saying "the major status code" here is a bit ambiguous as to whether
>> it is from initiator or acceptor (it's supposed to be the acceptor, if I
>> remember correctly.
>
> Only the initiator should make control flow decisions based on the
> status returned by GSS_Init_sec_context, and only the acceptor should
> make decisions based on the status returned by GSS_Accept_sec_context.
> RPC or not, the initiator isn't calling GSS_Accept_sec_context; the
> acceptor is.
>
> Among other things, the abstract GSS-API doesn't nail down the values of
> status codes, so they cannot be sent over the wire portably and thus the
> client cannot directly make decisions based on the status returned on
> the server.  Note that SSH doesn't require the client to interpret the

Okay.


>>>  (a) The server returned GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED, with
>>>  an output token, and the client was expecting a token.  In this case,
>>>  the client begins the next cycle.
>>>
>>>  (b) The server returned GSS_S_COMPLETE without an output token, and
>>>  the client was not expecting a token.  In this case, negotiation
>>>  succeeds.
>>>
>>>  (c) All other combinations are failures.
>>
>> This is a valid decomposition, but I would prefer to not introduce the
>> notion of the client "expecting" a token; instead, I like to refer only to
>> what has already happened.  Thus, "the most recent call to
>> GSS_Init_sec_Context() returned the major status code GSS_S_COMPLETE"
>> instead of "the client was not expecting a token".
>
> OK; I don't have a problem with that.  I found my formulation a bit less
> wordy, which made it a useful shorthand for discussion, but using the
> more precise form in the document makes sense.
>
>
>> Are you claiming that the breakdown in step 4 is an invalid decomposition?
>
> I believe it's incorrect.  In that breakdown, if the server asserts that
> another cycle is needed but the client's last call returns
> GSS_S_COMPLETE, then the loop terminates successfully.  But this is
> actually a failure condition.

The client will (must!) fail if there is no usable ClientInfo returned. 
I'm not sure that we need to be terribly concerned about whether we call 
this a negotiation failure or just a failure.

I'm not sure I understand your statement, though.

>> If the server is not GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED, it is in
>> error, of course (as your (c), my first bullet point).  If the client was
>> not expecting a token (second bullet point) and gave one to the server,
>> the server cannot be setting GSS_S_CONTINUE_NEEDED (right?)
>
> Sure it can.  The server is an untrusted adversary; its responses are
> not bound by the client's state machine.

I was afraid you were going to say that, when I was writing this up and 
made the realization.  Will fix.

>> I believe I used to have an explicit case for the client expecting a token
>> and the server not returning one, but Simon noted that we can just
>> continue the loop and the next Init_sec_context call will detect the
>> error.
>
> If the server asserts CONTINUE_NEEDED and does not send a token, that's
> always an error.  The SSH text appears to presume that an empty token is
> the same as no token at all.  I can't remember offhand if that's true,
> but we tried to be fairly careful writing that particular bit of text
> (well, actually, it was pretty bad at first, and then there was a round
> of careful editing and rewriting), so it probably is.

Do you think we should change all occurrences of "output token" to 
"non-empty output token"?

>>> GSSNegotiate is intended to be called over an unprotected channel.
>>> So, there is no security reason to favor reporting results via an
>>> output parameter instead of via an Rx abort.
>>
>> That text at the end of 6.2 was intended to call to mind the security
>> considerations 12.1.  I guess the description as written is not really
>> consistent with the class of errors for which the security consideration
>> applies, though.
>
> Indeed, looking at it, I think the text in 12.1 is a bit strong.  I
> imagine OpenAFS clients will have been told which security classes to
> expect a cell to support, but that may not be true for every client
> implementation ever and it may not be true for applications other than
> AFS (which do exist, even if none are widely deployed).  In fact, the
> sort of fallback 12.1 seeks to prohibit is fairly normal, and usually OK
> provided it is to a mechanism the client was willing to use to begin
> with.
>
> FWIW, there may be _non-security_ reasons to favor an output parameter
> over an abort, and I have no problem with that (but avoid synthesizing
> GSS errors, especially since the client can't actually interpret them
> anyway).

I don't think we should add a non-abort non-gss-status "error code" output 
parameter other than the one already in the ClientInfo.

I will work on updates to the document.

-Ben